<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>[go] Go语言编码规范 - Never Give Up</title>
  <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>

<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />

<meta name="theme-color" content="#f8f5ec" />
<meta name="msapplication-navbutton-color" content="#f8f5ec">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#f8f5ec">


<meta name="author" content="ZhangKQ" /><meta name="description" content="Go语言编码规范[Uber] Uber 是一家美国硅谷的科技公司，也是 Go 语言的早期 adopter。其开源了很多 golang 项目，诸如被 Gopher 圈熟知的 zap、jaeg" /><meta name="keywords" content="开发语言, 后端, golang, go语言, 语言编码规范, Uber" />






<meta name="generator" content="Hugo 0.92.0 with theme even" />


<link rel="canonical" href="https://blog.nevergiveup.tech/post/dev_language/golang/go%E8%AF%AD%E8%A8%80%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83uber/" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">



<link href="/sass/main.min.b5a744db6de49a86cadafb3b70f555ab443f83c307a483402259e94726b045ff.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.css" integrity="sha256-7TyXnr2YU040zfSP+rEcz29ggW4j56/ujTPwjMzyqFY=" crossorigin="anonymous">


<meta property="og:title" content="[go] Go语言编码规范" />
<meta property="og:description" content="Go语言编码规范[Uber] Uber 是一家美国硅谷的科技公司，也是 Go 语言的早期 adopter。其开源了很多 golang 项目，诸如被 Gopher 圈熟知的 zap、jaeg" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://blog.nevergiveup.tech/post/dev_language/golang/go%E8%AF%AD%E8%A8%80%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83uber/" /><meta property="article:section" content="post" />
<meta property="article:published_time" content="2021-05-15T15:37:56+08:00" />
<meta property="article:modified_time" content="2022-02-10T16:37:56+08:00" />

<meta itemprop="name" content="[go] Go语言编码规范">
<meta itemprop="description" content="Go语言编码规范[Uber] Uber 是一家美国硅谷的科技公司，也是 Go 语言的早期 adopter。其开源了很多 golang 项目，诸如被 Gopher 圈熟知的 zap、jaeg"><meta itemprop="datePublished" content="2021-05-15T15:37:56+08:00" />
<meta itemprop="dateModified" content="2022-02-10T16:37:56+08:00" />
<meta itemprop="wordCount" content="15179">
<meta itemprop="keywords" content="开发语言,后端,golang,go语言,语言编码规范,Uber," /><meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="[go] Go语言编码规范"/>
<meta name="twitter:description" content="Go语言编码规范[Uber] Uber 是一家美国硅谷的科技公司，也是 Go 语言的早期 adopter。其开源了很多 golang 项目，诸如被 Gopher 圈熟知的 zap、jaeg"/>

<!--[if lte IE 9]>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
<![endif]-->

<!--[if lt IE 9]>
  <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
<![endif]-->

</head>
<body>
  <div id="mobile-navbar" class="mobile-navbar">
  <div class="mobile-header-logo">
    <a href="/" class="logo">Never Give Up</a>
  </div>
  <div class="mobile-navbar-icon">
    <span></span>
    <span></span>
    <span></span>
  </div>
</div>
<nav id="mobile-menu" class="mobile-menu slideout-menu">
  <ul class="mobile-menu-list">
    <a href="/">
        <li class="mobile-menu-item">主页</li>
      </a><a href="/post/">
        <li class="mobile-menu-item">归档</li>
      </a><a href="/tags/">
        <li class="mobile-menu-item">标签</li>
      </a><a href="/categories/">
        <li class="mobile-menu-item">分类</li>
      </a><a href="/remark/">
        <li class="mobile-menu-item">随言碎语</li>
      </a><a href="/about/">
        <li class="mobile-menu-item">关于</li>
      </a>
  </ul>

  


</nav>

  <div class="container" id="mobile-panel">
    <header id="header" class="header">
        <div class="logo-wrapper">
  <a href="/" class="logo">Never Give Up</a>
</div>





<nav class="site-navbar">
  <ul id="menu" class="menu">
    <li class="menu-item">
        <a class="menu-item-link" href="/">主页</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/post/">归档</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/tags/">标签</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/categories/">分类</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/remark/">随言碎语</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/about/">关于</a>
      </li>
  </ul>
</nav>

    </header>

    <main id="main" class="main">
      <div class="content-wrapper">
        <div id="content" class="content">
          <article class="post">
    
    <header class="post-header">
      <h1 class="post-title">[go] Go语言编码规范</h1>

      <div class="post-meta">
        <span class="post-time"> 2021-05-15 </span>
        <div class="post-category">
            <a href="/categories/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/"> 开发语言 </a>
            <a href="/categories/golang/"> golang </a>
            </div>
          <span class="more-meta"> 约 15179 字 </span>
          <span class="more-meta"> 预计阅读 31 分钟 </span>
        
      </div>
    </header>

    <div class="post-toc" id="post-toc">
  <h2 class="post-toc-title">文章目录</h2>
  <div class="post-toc-content">
    <nav id="TableOfContents">
  <ul>
    <li><a href="#go语言编码规范uber">Go语言编码规范[Uber]</a>
      <ul>
        <li><a href="#版本">版本</a></li>
        <li><a href="#目录">目录</a></li>
        <li><a href="#介绍">介绍</a></li>
        <li><a href="#规范">规范</a>
          <ul>
            <li><a href="#一致性">一致性</a></li>
            <li><a href="#相似的声明放在一组">相似的声明放在一组</a></li>
            <li><a href="#import-分组">import 分组</a></li>
            <li><a href="#包名">包名</a></li>
            <li><a href="#函数名">函数名</a></li>
            <li><a href="#导入别名">导入别名</a></li>
            <li><a href="#函数分组与顺序">函数分组与顺序</a></li>
            <li><a href="#减少嵌套">减少嵌套</a></li>
            <li><a href="#不必要的-else">不必要的 else</a></li>
            <li><a href="#顶层变量声明">顶层变量声明</a></li>
            <li><a href="#对于未导出的顶层常量和变量使用_作为前缀">对于未导出的顶层常量和变量，使用_作为前缀</a></li>
            <li><a href="#结构体中的嵌入">结构体中的嵌入</a></li>
            <li><a href="#使用字段名初始化结构体">使用字段名初始化结构体</a></li>
            <li><a href="#本地变量声明">本地变量声明</a></li>
            <li><a href="#nil-是一个有效的-slice">nil 是一个有效的 slice</a></li>
            <li><a href="#缩小变量作用域">缩小变量作用域</a></li>
            <li><a href="#避免参数语义不明确avoid-naked-parameters">避免参数语义不明确(Avoid Naked Parameters)</a></li>
            <li><a href="#使用原始字符串字面值避免转义">使用原始字符串字面值，避免转义</a></li>
            <li><a href="#初始化-struct-引用">初始化 Struct 引用</a></li>
            <li><a href="#初始化-maps">初始化 Maps</a></li>
            <li><a href="#字符串-string-format">字符串 string format</a></li>
            <li><a href="#命名-printf-样式的函数">命名 Printf 样式的函数</a></li>
          </ul>
        </li>
        <li><a href="#指导原则">指导原则</a>
          <ul>
            <li><a href="#指向-interface-的指针">指向 interface 的指针</a></li>
            <li><a href="#interface-合理性验证">Interface 合理性验证</a></li>
            <li><a href="#接收器-receiver-与接口">接收器 (receiver) 与接口</a></li>
            <li><a href="#零值-mutex-是有效的">零值 Mutex 是有效的</a></li>
            <li><a href="#在边界处拷贝-slices-和-maps">在边界处拷贝 Slices 和 Maps</a></li>
            <li><a href="#使用-defer-释放资源">使用 defer 释放资源</a></li>
            <li><a href="#channel-的-size-要么是-1要么是无缓冲的">Channel 的 size 要么是 1，要么是无缓冲的</a></li>
            <li><a href="#枚举从-1-开始">枚举从 1 开始</a></li>
            <li><a href="#使用-time-处理时间">使用 time 处理时间</a></li>
            <li><a href="#错误类型">错误类型</a></li>
            <li><a href="#错误包装-error-wrapping">错误包装 (Error Wrapping)</a></li>
            <li><a href="#处理类型断言失败">处理类型断言失败</a></li>
            <li><a href="#不要-panic">不要 panic</a></li>
            <li><a href="#使用-gouberorgatomic">使用 go.uber.org/atomic</a></li>
            <li><a href="#避免可变全局变量">避免可变全局变量</a></li>
            <li><a href="#避免在公共结构中嵌入类型">避免在公共结构中嵌入类型</a></li>
            <li><a href="#避免使用内置名称">避免使用内置名称</a></li>
            <li><a href="#避免使用-init">避免使用 <code>init()</code></a></li>
            <li><a href="#追加时优先指定切片容量">追加时优先指定切片容量</a></li>
          </ul>
        </li>
        <li><a href="#性能">性能</a>
          <ul>
            <li><a href="#优先使用-strconv-而不是-fmt">优先使用 strconv 而不是 fmt</a></li>
            <li><a href="#避免字符串到字节的转换">避免字符串到字节的转换</a></li>
            <li><a href="#指定容器容量">指定容器容量</a></li>
          </ul>
        </li>
        <li><a href="#编程模式">编程模式</a>
          <ul>
            <li><a href="#表驱动测试">表驱动测试</a></li>
            <li><a href="#功能选项">功能选项</a></li>
          </ul>
        </li>
        <li><a href="#其他要求">其他要求</a>
          <ul>
            <li><a href="#通过检查">通过检查</a></li>
            <li><a href="#尽可能完善单元测试">尽可能完善单元测试</a></li>
            <li><a href="#完整的包注释">完整的包注释</a></li>
            <li><a href="#自定义接口结构体函数注释">自定义接口、结构体、函数注释</a></li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</nav>
  </div>
</div>
    <div class="post-content">
      <h1 id="go语言编码规范uber">Go语言编码规范[Uber]</h1>
<p><a href="https://www.uber.com/">Uber</a> 是一家美国硅谷的科技公司，也是 Go 语言的早期 adopter。其开源了很多 golang 项目，诸如被 Gopher 圈熟知的 <a href="https://github.com/uber-go/zap">zap</a>、<a href="https://github.com/jaegertracing/jaeger">jaeger</a> 等。2018 年年末 Uber 将内部的 <a href="https://github.com/uber-go/guide">Go 风格规范</a> 开源到 GitHub，经过一年的积累和更新，该规范已经初具规模，并受到广大 Gopher 的关注。本文是该规范的中文版本。本版本会根据原版实时更新。</p>
<h2 id="版本">版本</h2>
<ul>
<li>当前更新版本：2020-06-17 版本地址：<a href="https://github.com/uber-go/guide/commit/fdb233143f1276f33ccba1372588c3b9290d00f1">commit:#97</a></li>
<li>如果您发现任何更新、问题或改进，请随时 fork 和 PR</li>
<li>Please feel free to fork and PR if you find any updates, issues or improvement.</li>
</ul>
<h2 id="目录">目录</h2>
<ul>
<li><a href="#uber-goguide-%E7%9A%84%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91">uber-go/guide 的中文翻译</a></li>
<li><a href="#english">English</a></li>
<li><a href="#uber-go-%E8%AF%AD%E8%A8%80%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83">Uber Go 语言编码规范</a>
<ul>
<li><a href="#%E7%89%88%E6%9C%AC">版本</a></li>
<li><a href="#%E7%9B%AE%E5%BD%95">目录</a></li>
<li><a href="#%E4%BB%8B%E7%BB%8D">介绍</a></li>
<li><a href="#%E8%A7%84%E8%8C%83">规范</a>
<ul>
<li><a href="#%E4%B8%80%E8%87%B4%E6%80%A7">一致性</a></li>
<li><a href="#%E7%9B%B8%E4%BC%BC%E7%9A%84%E5%A3%B0%E6%98%8E%E6%94%BE%E5%9C%A8%E4%B8%80%E7%BB%84">相似的声明放在一组</a></li>
<li><a href="#import-%E5%88%86%E7%BB%84">import 分组</a></li>
<li><a href="#%E5%8C%85%E5%90%8D">包名</a></li>
<li><a href="#%E5%87%BD%E6%95%B0%E5%90%8D">函数名</a></li>
<li><a href="#%E5%AF%BC%E5%85%A5%E5%88%AB%E5%90%8D">导入别名</a></li>
<li><a href="#%E5%87%BD%E6%95%B0%E5%88%86%E7%BB%84%E4%B8%8E%E9%A1%BA%E5%BA%8F">函数分组与顺序</a></li>
<li><a href="#%E5%87%8F%E5%B0%91%E5%B5%8C%E5%A5%97">减少嵌套</a></li>
<li><a href="#%E4%B8%8D%E5%BF%85%E8%A6%81%E7%9A%84-else">不必要的 else</a></li>
<li><a href="#%E9%A1%B6%E5%B1%82%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E">顶层变量声明</a></li>
<li><a href="#%E5%AF%B9%E4%BA%8E%E6%9C%AA%E5%AF%BC%E5%87%BA%E7%9A%84%E9%A1%B6%E5%B1%82%E5%B8%B8%E9%87%8F%E5%92%8C%E5%8F%98%E9%87%8F%E4%BD%BF%E7%94%A8_%E4%BD%9C%E4%B8%BA%E5%89%8D%E7%BC%80">对于未导出的顶层常量和变量，使用_作为前缀</a></li>
<li><a href="#%E7%BB%93%E6%9E%84%E4%BD%93%E4%B8%AD%E7%9A%84%E5%B5%8C%E5%85%A5">结构体中的嵌入</a></li>
<li><a href="#%E4%BD%BF%E7%94%A8%E5%AD%97%E6%AE%B5%E5%90%8D%E5%88%9D%E5%A7%8B%E5%8C%96%E7%BB%93%E6%9E%84%E4%BD%93">使用字段名初始化结构体</a></li>
<li><a href="#%E6%9C%AC%E5%9C%B0%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E">本地变量声明</a></li>
<li><a href="#nil-%E6%98%AF%E4%B8%80%E4%B8%AA%E6%9C%89%E6%95%88%E7%9A%84-slice">nil 是一个有效的 slice</a></li>
<li><a href="#%E7%BC%A9%E5%B0%8F%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F">缩小变量作用域</a></li>
<li><a href="#%E9%81%BF%E5%85%8D%E5%8F%82%E6%95%B0%E8%AF%AD%E4%B9%89%E4%B8%8D%E6%98%8E%E7%A1%AEavoid-naked-parameters">避免参数语义不明确(Avoid Naked Parameters)</a></li>
<li><a href="#%E4%BD%BF%E7%94%A8%E5%8E%9F%E5%A7%8B%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E5%80%BC%E9%81%BF%E5%85%8D%E8%BD%AC%E4%B9%89">使用原始字符串字面值，避免转义</a></li>
<li><a href="#%E5%88%9D%E5%A7%8B%E5%8C%96-struct-%E5%BC%95%E7%94%A8">初始化 Struct 引用</a></li>
<li><a href="#%E5%88%9D%E5%A7%8B%E5%8C%96-maps">初始化 Maps</a></li>
<li><a href="#%E5%AD%97%E7%AC%A6%E4%B8%B2-string-format">字符串 string format</a></li>
<li><a href="#%E5%91%BD%E5%90%8D-printf-%E6%A0%B7%E5%BC%8F%E7%9A%84%E5%87%BD%E6%95%B0">命名 Printf 样式的函数</a></li>
</ul>
</li>
<li><a href="#%E6%8C%87%E5%AF%BC%E5%8E%9F%E5%88%99">指导原则</a>
<ul>
<li><a href="#%E6%8C%87%E5%90%91-interface-%E7%9A%84%E6%8C%87%E9%92%88">指向 interface 的指针</a></li>
<li><a href="#interface-%E5%90%88%E7%90%86%E6%80%A7%E9%AA%8C%E8%AF%81">Interface 合理性验证</a></li>
<li><a href="#%E6%8E%A5%E6%94%B6%E5%99%A8-receiver-%E4%B8%8E%E6%8E%A5%E5%8F%A3">接收器 (receiver) 与接口</a></li>
<li><a href="#%E9%9B%B6%E5%80%BC-mutex-%E6%98%AF%E6%9C%89%E6%95%88%E7%9A%84">零值 Mutex 是有效的</a></li>
<li><a href="#%E5%9C%A8%E8%BE%B9%E7%95%8C%E5%A4%84%E6%8B%B7%E8%B4%9D-slices-%E5%92%8C-maps">在边界处拷贝 Slices 和 Maps</a>
<ul>
<li><a href="#%E6%8E%A5%E6%94%B6-slices-%E5%92%8C-maps">接收 Slices 和 Maps</a></li>
<li><a href="#%E8%BF%94%E5%9B%9E-slices-%E6%88%96-maps">返回 slices 或 maps</a></li>
</ul>
</li>
<li><a href="#%E4%BD%BF%E7%94%A8-defer-%E9%87%8A%E6%94%BE%E8%B5%84%E6%BA%90">使用 defer 释放资源</a></li>
<li><a href="#channel-%E7%9A%84-size-%E8%A6%81%E4%B9%88%E6%98%AF-1%E8%A6%81%E4%B9%88%E6%98%AF%E6%97%A0%E7%BC%93%E5%86%B2%E7%9A%84">Channel 的 size 要么是 1，要么是无缓冲的</a></li>
<li><a href="#%E6%9E%9A%E4%B8%BE%E4%BB%8E-1-%E5%BC%80%E5%A7%8B">枚举从 1 开始</a></li>
<li><a href="#%E4%BD%BF%E7%94%A8-time-%E5%A4%84%E7%90%86%E6%97%B6%E9%97%B4">使用 time 处理时间</a>
<ul>
<li><a href="#%E4%BD%BF%E7%94%A8-timetime-%E8%A1%A8%E8%BE%BE%E7%9E%AC%E6%97%B6%E6%97%B6%E9%97%B4">使用 <code>time.Time</code> 表达瞬时时间</a></li>
<li><a href="#%E4%BD%BF%E7%94%A8-timeduration-%E8%A1%A8%E8%BE%BE%E6%97%B6%E9%97%B4%E6%AE%B5">使用 <code>time.Duration</code> 表达时间段</a></li>
<li><a href="#%E5%AF%B9%E5%A4%96%E9%83%A8%E7%B3%BB%E7%BB%9F%E4%BD%BF%E7%94%A8-timetime-%E5%92%8C-timeduration">对外部系统使用 <code>time.Time</code> 和 <code>time.Duration</code></a></li>
</ul>
</li>
<li><a href="#%E9%94%99%E8%AF%AF%E7%B1%BB%E5%9E%8B">错误类型</a></li>
<li><a href="#%E9%94%99%E8%AF%AF%E5%8C%85%E8%A3%85-error-wrapping">错误包装 (Error Wrapping)</a></li>
<li><a href="#%E5%A4%84%E7%90%86%E7%B1%BB%E5%9E%8B%E6%96%AD%E8%A8%80%E5%A4%B1%E8%B4%A5">处理类型断言失败</a></li>
<li><a href="#%E4%B8%8D%E8%A6%81-panic">不要 panic</a></li>
<li><a href="#%E4%BD%BF%E7%94%A8-gouberorgatomic">使用 go.uber.org/atomic</a></li>
<li><a href="#%E9%81%BF%E5%85%8D%E5%8F%AF%E5%8F%98%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F">避免可变全局变量</a></li>
<li><a href="#%E9%81%BF%E5%85%8D%E5%9C%A8%E5%85%AC%E5%85%B1%E7%BB%93%E6%9E%84%E4%B8%AD%E5%B5%8C%E5%85%A5%E7%B1%BB%E5%9E%8B">避免在公共结构中嵌入类型</a></li>
<li><a href="#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8%E5%86%85%E7%BD%AE%E5%90%8D%E7%A7%B0">避免使用内置名称</a></li>
<li><a href="#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8-init">避免使用 <code>init()</code></a></li>
<li><a href="#%E8%BF%BD%E5%8A%A0%E6%97%B6%E4%BC%98%E5%85%88%E6%8C%87%E5%AE%9A%E5%88%87%E7%89%87%E5%AE%B9%E9%87%8F">追加时优先指定切片容量</a></li>
</ul>
</li>
<li><a href="#%E6%80%A7%E8%83%BD">性能</a>
<ul>
<li><a href="#%E4%BC%98%E5%85%88%E4%BD%BF%E7%94%A8-strconv-%E8%80%8C%E4%B8%8D%E6%98%AF-fmt">优先使用 strconv 而不是 fmt</a></li>
<li><a href="#%E9%81%BF%E5%85%8D%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%B0%E5%AD%97%E8%8A%82%E7%9A%84%E8%BD%AC%E6%8D%A2">避免字符串到字节的转换</a></li>
<li><a href="#%E6%8C%87%E5%AE%9A%E5%AE%B9%E5%99%A8%E5%AE%B9%E9%87%8F">指定容器容量</a>
<ul>
<li><a href="#%E6%8C%87%E5%AE%9Amap%E5%AE%B9%E9%87%8F%E6%8F%90%E7%A4%BA">指定Map容量提示</a></li>
<li><a href="#%E6%8C%87%E5%AE%9A%E5%88%87%E7%89%87%E5%AE%B9%E9%87%8F">指定切片容量</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#%E7%BC%96%E7%A8%8B%E6%A8%A1%E5%BC%8F">编程模式</a>
<ul>
<li><a href="#%E8%A1%A8%E9%A9%B1%E5%8A%A8%E6%B5%8B%E8%AF%95">表驱动测试</a></li>
<li><a href="#%E5%8A%9F%E8%83%BD%E9%80%89%E9%A1%B9">功能选项</a></li>
</ul>
</li>
<li><a href="#%E5%85%B6%E4%BB%96%E8%A6%81%E6%B1%82">其他要求</a>
<ul>
<li><a href="#%E9%80%9A%E8%BF%87%E6%A3%80%E6%9F%A5">通过检查</a></li>
<li><a href="#%E5%B0%BD%E5%8F%AF%E8%83%BD%E5%AE%8C%E5%96%84%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95">尽可能完善单元测试</a></li>
<li><a href="#%E5%AE%8C%E6%95%B4%E7%9A%84%E5%8C%85%E6%B3%A8%E9%87%8A">完整的包注释</a></li>
<li><a href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84%E4%BD%93%E5%87%BD%E6%95%B0%E6%B3%A8%E9%87%8A">自定义接口、结构体、函数注释</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="介绍">介绍</h2>
<p>样式 (style) 是支配我们代码的惯例。术语<code>样式</code>有点用词不当，因为这些约定涵盖的范围不限于由 gofmt 替我们处理的源文件格式。</p>
<p>本指南的目的是通过详细描述在 Uber 编写 Go 代码的注意事项来管理这种复杂性。这些规则的存在是为了使代码库易于管理，同时仍然允许工程师更有效地使用 Go 语言功能。</p>
<p>该指南最初由 <a href="https://github.com/prashantv">Prashant Varanasi</a> 和 <a href="https://github.com/nomis52">Simon Newton</a> 编写，目的是使一些同事能快速使用 Go。多年来，该指南已根据其他人的反馈进行了修改。</p>
<p>本文档记录了我们在 Uber 遵循的 Go 代码中的惯用约定。其中许多是 Go 的通用准则，而其他扩展准则依赖于下面外部的指南：</p>
<ol>
<li><a href="https://golang.org/doc/effective_go.html">Effective Go</a></li>
<li><a href="https://github.com/golang/go/wiki/CodeReviewComments">The Go common mistakes guide</a></li>
</ol>
<p>所有代码都应该通过<code>golint</code>和<code>go vet</code>的检查并无错误。我们建议您将编辑器设置为：</p>
<ul>
<li>保存时运行 <code>goimports</code></li>
<li>运行 <code>golint</code> 和 <code>go vet</code> 检查错误</li>
</ul>
<p>您可以在以下 Go 编辑器工具支持页面中找到更为详细的信息：
<a href="https://github.com/golang/go/wiki/IDEsAndTextEditorPlugins">https://github.com/golang/go/wiki/IDEsAndTextEditorPlugins</a></p>
<h2 id="规范">规范</h2>
<h3 id="一致性">一致性</h3>
<p>本文中概述的一些标准都是客观性的评估，是根据场景、上下文、或者主观性的判断；</p>
<p>但是最重要的是，<strong>保持一致</strong>.</p>
<p>一致性的代码更容易维护、是更合理的、需要更少的学习成本、并且随着新的约定出现或者出现错误后更容易迁移、更新、修复 bug</p>
<p>相反，一个单一的代码库会导致维护成本开销、不确定性和认知偏差。所有这些都会直接导致速度降低、
代码审查痛苦、而且增加 bug 数量</p>
<p>将这些标准应用于代码库时，建议在 package（或更大）级别进行更改，子包级别的应用程序通过将多个样式引入到同一代码中，违反了上述关注点。</p>
<h3 id="相似的声明放在一组">相似的声明放在一组</h3>
<p>Go 语言支持将相似的声明放在一个组内。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span> <span class="s">&#34;a&#34;</span>
<span class="kn">import</span> <span class="s">&#34;b&#34;</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span> <span class="p">(</span>
  <span class="s">&#34;a&#34;</span>
  <span class="s">&#34;b&#34;</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>这同样适用于常量、变量和类型声明：</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go">
<span class="kd">const</span> <span class="nx">a</span> <span class="p">=</span> <span class="mi">1</span>
<span class="kd">const</span> <span class="nx">b</span> <span class="p">=</span> <span class="mi">2</span>

<span class="kd">var</span> <span class="nx">a</span> <span class="p">=</span> <span class="mi">1</span>
<span class="kd">var</span> <span class="nx">b</span> <span class="p">=</span> <span class="mi">2</span>

<span class="kd">type</span> <span class="nx">Area</span> <span class="kt">float64</span>
<span class="kd">type</span> <span class="nx">Volume</span> <span class="kt">float64</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">const</span> <span class="p">(</span>
  <span class="nx">a</span> <span class="p">=</span> <span class="mi">1</span>
  <span class="nx">b</span> <span class="p">=</span> <span class="mi">2</span>
<span class="p">)</span>

<span class="kd">var</span> <span class="p">(</span>
  <span class="nx">a</span> <span class="p">=</span> <span class="mi">1</span>
  <span class="nx">b</span> <span class="p">=</span> <span class="mi">2</span>
<span class="p">)</span>

<span class="kd">type</span> <span class="p">(</span>
  <span class="nx">Area</span> <span class="kt">float64</span>
  <span class="nx">Volume</span> <span class="kt">float64</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>仅将相关的声明放在一组。不要将不相关的声明放在一组。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Operation</span> <span class="kt">int</span>

<span class="kd">const</span> <span class="p">(</span>
  <span class="nx">Add</span> <span class="nx">Operation</span> <span class="p">=</span> <span class="kc">iota</span> <span class="o">+</span> <span class="mi">1</span>
  <span class="nx">Subtract</span>
  <span class="nx">Multiply</span>
  <span class="nx">ENV_VAR</span> <span class="p">=</span> <span class="s">&#34;MY_ENV&#34;</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Operation</span> <span class="kt">int</span>

<span class="kd">const</span> <span class="p">(</span>
  <span class="nx">Add</span> <span class="nx">Operation</span> <span class="p">=</span> <span class="kc">iota</span> <span class="o">+</span> <span class="mi">1</span>
  <span class="nx">Subtract</span>
  <span class="nx">Multiply</span>
<span class="p">)</span>

<span class="kd">const</span> <span class="nx">ENV_VAR</span> <span class="p">=</span> <span class="s">&#34;MY_ENV&#34;</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>分组使用的位置没有限制，例如：你可以在函数内部使用它们：</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">red</span> <span class="p">=</span> <span class="nx">color</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mh">0xff0000</span><span class="p">)</span>
  <span class="kd">var</span> <span class="nx">green</span> <span class="p">=</span> <span class="nx">color</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mh">0x00ff00</span><span class="p">)</span>
  <span class="kd">var</span> <span class="nx">blue</span> <span class="p">=</span> <span class="nx">color</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mh">0x0000ff</span><span class="p">)</span>

  <span class="o">...</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="p">(</span>
    <span class="nx">red</span>   <span class="p">=</span> <span class="nx">color</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mh">0xff0000</span><span class="p">)</span>
    <span class="nx">green</span> <span class="p">=</span> <span class="nx">color</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mh">0x00ff00</span><span class="p">)</span>
    <span class="nx">blue</span>  <span class="p">=</span> <span class="nx">color</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="mh">0x0000ff</span><span class="p">)</span>
  <span class="p">)</span>

  <span class="o">...</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="import-分组">import 分组</h3>
<p>导入应该分为两组：</p>
<ul>
<li>标准库</li>
<li>其他库</li>
</ul>
<p>默认情况下，这是 goimports 应用的分组。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span> <span class="p">(</span>
  <span class="s">&#34;fmt&#34;</span>
  <span class="s">&#34;os&#34;</span>
  <span class="s">&#34;go.uber.org/atomic&#34;</span>
  <span class="s">&#34;golang.org/x/sync/errgroup&#34;</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span> <span class="p">(</span>
  <span class="s">&#34;fmt&#34;</span>
  <span class="s">&#34;os&#34;</span>

  <span class="s">&#34;go.uber.org/atomic&#34;</span>
  <span class="s">&#34;golang.org/x/sync/errgroup&#34;</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="包名">包名</h3>
<p>当命名包时，请按下面规则选择一个名称：</p>
<ul>
<li>全部小写。没有大写或下划线。</li>
<li>大多数使用命名导入的情况下，不需要重命名。</li>
<li>简短而简洁。请记住，在每个使用的地方都完整标识了该名称。</li>
<li>不用复数。例如<code>net/url</code>，而不是<code>net/urls</code>。</li>
<li>不要用“common”，“util”，“shared”或“lib”。这些是不好的，信息量不足的名称。</li>
</ul>
<p>另请参阅 <a href="https://blog.golang.org/package-names">Package Names</a> 和 <a href="https://rakyll.org/style-packages/">Go 包样式指南</a>.</p>
<h3 id="函数名">函数名</h3>
<p>我们遵循 Go 社区关于使用 <a href="https://golang.org/doc/effective_go.html#mixed-caps">MixedCaps 作为函数名</a> 的约定。有一个例外，为了对相关的测试用例进行分组，函数名可能包含下划线，如：<code>TestMyFunction_WhatIsBeingTested</code>.</p>
<h3 id="导入别名">导入别名</h3>
<p>如果程序包名称与导入路径的最后一个元素不匹配，则必须使用导入别名。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span> <span class="p">(</span>
  <span class="s">&#34;net/http&#34;</span>

  <span class="nx">client</span> <span class="s">&#34;example.com/client-go&#34;</span>
  <span class="nx">trace</span> <span class="s">&#34;example.com/trace/v2&#34;</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p>在所有其他情况下，除非导入之间有直接冲突，否则应避免导入别名。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span> <span class="p">(</span>
  <span class="s">&#34;fmt&#34;</span>
  <span class="s">&#34;os&#34;</span>

  <span class="nx">nettrace</span> <span class="s">&#34;golang.net/x/trace&#34;</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span> <span class="p">(</span>
  <span class="s">&#34;fmt&#34;</span>
  <span class="s">&#34;os&#34;</span>
  <span class="s">&#34;runtime/trace&#34;</span>

  <span class="nx">nettrace</span> <span class="s">&#34;golang.net/x/trace&#34;</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="函数分组与顺序">函数分组与顺序</h3>
<ul>
<li>函数应按粗略的调用顺序排序。</li>
<li>同一文件中的函数应按接收者分组。</li>
</ul>
<p>因此，导出的函数应先出现在文件中，放在<code>struct</code>, <code>const</code>, <code>var</code>定义的后面。</p>
<p>在定义类型之后，但在接收者的其余方法之前，可能会出现一个 <code>newXYZ()</code>/<code>NewXYZ()</code></p>
<p>由于函数是按接收者分组的，因此普通工具函数应在文件末尾出现。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">something</span><span class="p">)</span> <span class="nf">Cost</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nf">calcCost</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nx">weights</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">something</span> <span class="kd">struct</span><span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="kd">func</span> <span class="nf">calcCost</span><span class="p">(</span><span class="nx">n</span> <span class="p">[]</span><span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">something</span><span class="p">)</span> <span class="nf">Stop</span><span class="p">()</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>

<span class="kd">func</span> <span class="nf">newSomething</span><span class="p">()</span> <span class="o">*</span><span class="nx">something</span> <span class="p">{</span>
    <span class="k">return</span> <span class="o">&amp;</span><span class="nx">something</span><span class="p">{}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">something</span> <span class="kd">struct</span><span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="kd">func</span> <span class="nf">newSomething</span><span class="p">()</span> <span class="o">*</span><span class="nx">something</span> <span class="p">{</span>
    <span class="k">return</span> <span class="o">&amp;</span><span class="nx">something</span><span class="p">{}</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">something</span><span class="p">)</span> <span class="nf">Cost</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nf">calcCost</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nx">weights</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">something</span><span class="p">)</span> <span class="nf">Stop</span><span class="p">()</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>

<span class="kd">func</span> <span class="nf">calcCost</span><span class="p">(</span><span class="nx">n</span> <span class="p">[]</span><span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="减少嵌套">减少嵌套</h3>
<p>代码应通过尽可能先处理错误情况/特殊情况并尽早返回或继续循环来减少嵌套。减少嵌套多个级别的代码的代码量。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">data</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nx">v</span><span class="p">.</span><span class="nx">F1</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span>
    <span class="nx">v</span> <span class="p">=</span> <span class="nf">process</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span>
    <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">v</span><span class="p">.</span><span class="nf">Call</span><span class="p">();</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
      <span class="nx">v</span><span class="p">.</span><span class="nf">Send</span><span class="p">()</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">err</span>
    <span class="p">}</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Invalid v: %v&#34;</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">data</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nx">v</span><span class="p">.</span><span class="nx">F1</span> <span class="o">!=</span> <span class="mi">1</span> <span class="p">{</span>
    <span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Invalid v: %v&#34;</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span>
    <span class="k">continue</span>
  <span class="p">}</span>

  <span class="nx">v</span> <span class="p">=</span> <span class="nf">process</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span>
  <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">v</span><span class="p">.</span><span class="nf">Call</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">err</span>
  <span class="p">}</span>
  <span class="nx">v</span><span class="p">.</span><span class="nf">Send</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="不必要的-else">不必要的 else</h3>
<p>如果在 if 的两个分支中都设置了变量，则可以将其替换为单个 if。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">a</span> <span class="kt">int</span>
<span class="k">if</span> <span class="nx">b</span> <span class="p">{</span>
  <span class="nx">a</span> <span class="p">=</span> <span class="mi">100</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="nx">a</span> <span class="p">=</span> <span class="mi">10</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">a</span> <span class="o">:=</span> <span class="mi">10</span>
<span class="k">if</span> <span class="nx">b</span> <span class="p">{</span>
  <span class="nx">a</span> <span class="p">=</span> <span class="mi">100</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="顶层变量声明">顶层变量声明</h3>
<p>在顶层，使用标准<code>var</code>关键字。请勿指定类型，除非它与表达式的类型不同。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">_s</span> <span class="kt">string</span> <span class="p">=</span> <span class="nf">F</span><span class="p">()</span>

<span class="kd">func</span> <span class="nf">F</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span> <span class="k">return</span> <span class="s">&#34;A&#34;</span> <span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">_s</span> <span class="p">=</span> <span class="nf">F</span><span class="p">()</span>
<span class="c1">// 由于 F 已经明确了返回一个字符串类型，因此我们没有必要显式指定_s 的类型
</span><span class="c1">// 还是那种类型
</span><span class="c1"></span>
<span class="kd">func</span> <span class="nf">F</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span> <span class="k">return</span> <span class="s">&#34;A&#34;</span> <span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>如果表达式的类型与所需的类型不完全匹配，请指定类型。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">myError</span> <span class="kd">struct</span><span class="p">{}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">myError</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span> <span class="k">return</span> <span class="s">&#34;error&#34;</span> <span class="p">}</span>

<span class="kd">func</span> <span class="nf">F</span><span class="p">()</span> <span class="nx">myError</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">myError</span><span class="p">{}</span> <span class="p">}</span>

<span class="kd">var</span> <span class="nx">_e</span> <span class="kt">error</span> <span class="p">=</span> <span class="nf">F</span><span class="p">()</span>
<span class="c1">// F 返回一个 myError 类型的实例，但是我们要 error 类型
</span></code></pre></td></tr></table>
</div>
</div><h3 id="对于未导出的顶层常量和变量使用_作为前缀">对于未导出的顶层常量和变量，使用_作为前缀</h3>
<p>在未导出的顶级<code>vars</code>和<code>consts</code>， 前面加上前缀_，以使它们在使用时明确表示它们是全局符号。</p>
<p>例外：未导出的错误值，应以<code>err</code>开头。</p>
<p>基本依据：顶级变量和常量具有包范围作用域。使用通用名称可能很容易在其他文件中意外使用错误的值。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// foo.go
</span><span class="c1"></span>
<span class="kd">const</span> <span class="p">(</span>
  <span class="nx">defaultPort</span> <span class="p">=</span> <span class="mi">8080</span>
  <span class="nx">defaultUser</span> <span class="p">=</span> <span class="s">&#34;user&#34;</span>
<span class="p">)</span>

<span class="c1">// bar.go
</span><span class="c1"></span>
<span class="kd">func</span> <span class="nf">Bar</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">defaultPort</span> <span class="o">:=</span> <span class="mi">9090</span>
  <span class="o">...</span>
  <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Default port&#34;</span><span class="p">,</span> <span class="nx">defaultPort</span><span class="p">)</span>

  <span class="c1">// We will not see a compile error if the first line of
</span><span class="c1"></span>  <span class="c1">// Bar() is deleted.
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// foo.go
</span><span class="c1"></span>
<span class="kd">const</span> <span class="p">(</span>
  <span class="nx">_defaultPort</span> <span class="p">=</span> <span class="mi">8080</span>
  <span class="nx">_defaultUser</span> <span class="p">=</span> <span class="s">&#34;user&#34;</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="结构体中的嵌入">结构体中的嵌入</h3>
<p>嵌入式类型（例如 mutex）应位于结构体内的字段列表的顶部，并且必须有一个空行将嵌入式字段与常规字段分隔开。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Client</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">version</span> <span class="kt">int</span>
  <span class="nx">http</span><span class="p">.</span><span class="nx">Client</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Client</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">http</span><span class="p">.</span><span class="nx">Client</span>

  <span class="nx">version</span> <span class="kt">int</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>内嵌应该提供切实的好处，比如以语义上合适的方式添加或增强功能。
它应该在对用户不利影响的情况下完成这项工作（另请参见：<code>避免在公共结构中嵌入类型</code><a href="#avoid-embedding-types-in-public-structs">Avoid Embedding Types in Public Structs</a>）。</p>
<p>嵌入 <strong>不应该</strong>:</p>
<ul>
<li>纯粹是为了美观或方便。</li>
<li>使外部类型更难构造或使用。</li>
<li>影响外部类型的零值。如果外部类型有一个有用的零值，则在嵌入内部类型之后应该仍然有一个有用的零值。</li>
<li>作为嵌入内部类型的副作用，从外部类型公开不相关的函数或字段。</li>
<li>公开未导出的类型。</li>
<li>影响外部类型的复制形式。</li>
<li>更改外部类型的API或类型语义。</li>
<li>嵌入内部类型的非规范形式。</li>
<li>公开外部类型的实现详细信息。</li>
<li>允许用户观察或控制类型内部。</li>
<li>通过包装的方式改变内部函数的一般行为，这种包装方式会给用户带来一些意料之外情况。</li>
</ul>
<p>简单地说，有意识地和有意识地嵌入。一种很好的测试体验是，
&ldquo;是否所有这些导出的内部方法/字段都将直接添加到外部类型&rdquo;
如果答案是<code>some</code>或<code>no</code>，不要嵌入内部类型-而是使用字段。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">A</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="c1">// Bad: A.Lock() and A.Unlock() 现在可用
</span><span class="c1"></span>    <span class="c1">// 不提供任何功能性好处，并允许用户控制有关A的内部细节。
</span><span class="c1"></span>    <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">countingWriteCloser</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="c1">// Good: Write() 在外层提供用于特定目的，
</span><span class="c1"></span>    <span class="c1">// 并且委托工作到内部类型的Write()中。
</span><span class="c1"></span>    <span class="nx">io</span><span class="p">.</span><span class="nx">WriteCloser</span>
    <span class="nx">count</span> <span class="kt">int</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="p">(</span><span class="nx">w</span> <span class="o">*</span><span class="nx">countingWriteCloser</span><span class="p">)</span> <span class="nf">Write</span><span class="p">(</span><span class="nx">bs</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">w</span><span class="p">.</span><span class="nx">count</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">bs</span><span class="p">)</span>
    <span class="k">return</span> <span class="nx">w</span><span class="p">.</span><span class="nx">WriteCloser</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">bs</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Book</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="c1">// Bad: 指针更改零值的有用性
</span><span class="c1"></span>    <span class="nx">io</span><span class="p">.</span><span class="nx">ReadWriter</span>
    <span class="c1">// other fields
</span><span class="c1"></span><span class="p">}</span>
<span class="c1">// later
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">b</span> <span class="nx">Book</span>
<span class="nx">b</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>  <span class="c1">// panic: nil pointer
</span><span class="c1"></span><span class="nx">b</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span>   <span class="c1">// panic: nil pointer
</span><span class="c1"></span><span class="nx">b</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="c1">// panic: nil pointer
</span></code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Book</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="c1">// Good: 有用的零值
</span><span class="c1"></span>    <span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span>
    <span class="c1">// other fields
</span><span class="c1"></span><span class="p">}</span>
<span class="c1">// later
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">b</span> <span class="nx">Book</span>
<span class="nx">b</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>  <span class="c1">// ok
</span><span class="c1"></span><span class="nx">b</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span>   <span class="c1">// ok
</span><span class="c1"></span><span class="nx">b</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="c1">// ok
</span></code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Client</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
    <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span>
    <span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span>
    <span class="nx">url</span><span class="p">.</span><span class="nx">URL</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Client</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="nx">mtx</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
    <span class="nx">wg</span>  <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span>
    <span class="nx">buf</span> <span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span>
    <span class="nx">url</span> <span class="nx">url</span><span class="p">.</span><span class="nx">URL</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="使用字段名初始化结构体">使用字段名初始化结构体</h3>
<p>初始化结构体时，应该指定字段名称。现在由 <a href="https://golang.org/cmd/vet/"><code>go vet</code></a> 强制执行。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">k</span> <span class="o">:=</span> <span class="nx">User</span><span class="p">{</span><span class="s">&#34;John&#34;</span><span class="p">,</span> <span class="s">&#34;Doe&#34;</span><span class="p">,</span> <span class="kc">true</span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">k</span> <span class="o">:=</span> <span class="nx">User</span><span class="p">{</span>
    <span class="nx">FirstName</span><span class="p">:</span> <span class="s">&#34;John&#34;</span><span class="p">,</span>
    <span class="nx">LastName</span><span class="p">:</span> <span class="s">&#34;Doe&#34;</span><span class="p">,</span>
    <span class="nx">Admin</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>例外：如果有 3 个或更少的字段，则可以在测试表中省略字段名称。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">tests</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span><span class="p">{</span>
  <span class="nx">op</span> <span class="nx">Operation</span>
  <span class="nx">want</span> <span class="kt">string</span>
<span class="p">}{</span>
  <span class="p">{</span><span class="nx">Add</span><span class="p">,</span> <span class="s">&#34;add&#34;</span><span class="p">},</span>
  <span class="p">{</span><span class="nx">Subtract</span><span class="p">,</span> <span class="s">&#34;subtract&#34;</span><span class="p">},</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h3 id="本地变量声明">本地变量声明</h3>
<p>如果将变量明确设置为某个值，则应使用短变量声明形式 (<code>:=</code>)。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">s</span> <span class="p">=</span> <span class="s">&#34;foo&#34;</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">s</span> <span class="o">:=</span> <span class="s">&#34;foo&#34;</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>但是，在某些情况下，<code>var</code> 使用关键字时默认值会更清晰。例如，声明空切片。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">f</span><span class="p">(</span><span class="nx">list</span> <span class="p">[]</span><span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">filtered</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">int</span><span class="p">{}</span>
  <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">list</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nx">v</span> <span class="p">&gt;</span> <span class="mi">10</span> <span class="p">{</span>
      <span class="nx">filtered</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">filtered</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">f</span><span class="p">(</span><span class="nx">list</span> <span class="p">[]</span><span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">filtered</span> <span class="p">[]</span><span class="kt">int</span>
  <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">list</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nx">v</span> <span class="p">&gt;</span> <span class="mi">10</span> <span class="p">{</span>
      <span class="nx">filtered</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">filtered</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="nil-是一个有效的-slice">nil 是一个有效的 slice</h3>
<p><code>nil</code> 是一个有效的长度为 0 的 slice，这意味着，</p>
<ul>
<li>
<p>您不应明确返回长度为零的切片。应该返回<code>nil</code> 来代替。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">if</span> <span class="nx">x</span> <span class="o">==</span> <span class="s">&#34;&#34;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">[]</span><span class="kt">int</span><span class="p">{}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">if</span> <span class="nx">x</span> <span class="o">==</span> <span class="s">&#34;&#34;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
</li>
<li>
<p>要检查切片是否为空，请始终使用<code>len(s) == 0</code>。而非 <code>nil</code>。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">isEmpty</span><span class="p">(</span><span class="nx">s</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">s</span> <span class="o">==</span> <span class="kc">nil</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">isEmpty</span><span class="p">(</span><span class="nx">s</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
</li>
<li>
<p>零值切片（用<code>var</code>声明的切片）可立即使用，无需调用<code>make()</code>创建。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">nums</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">int</span><span class="p">{}</span>
<span class="c1">// or, nums := make([]int)
</span><span class="c1"></span>
<span class="k">if</span> <span class="nx">add1</span> <span class="p">{</span>
  <span class="nx">nums</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">nums</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">if</span> <span class="nx">add2</span> <span class="p">{</span>
  <span class="nx">nums</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">nums</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">nums</span> <span class="p">[]</span><span class="kt">int</span>

<span class="k">if</span> <span class="nx">add1</span> <span class="p">{</span>
  <span class="nx">nums</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">nums</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">if</span> <span class="nx">add2</span> <span class="p">{</span>
  <span class="nx">nums</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">nums</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
</li>
</ul>
<p>记住，虽然nil切片是有效的切片，但它不等于长度为0的切片（一个为nil，另一个不是），并且在不同的情况下（例如序列化），这两个切片的处理方式可能不同。</p>
<h3 id="缩小变量作用域">缩小变量作用域</h3>
<p>如果有可能，尽量缩小变量作用范围。除非它与 <a href="#%E5%87%8F%E5%B0%91%E5%B5%8C%E5%A5%97">减少嵌套</a>的规则冲突。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">WriteFile</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">data</span><span class="p">,</span> <span class="mo">0644</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
 <span class="k">return</span> <span class="nx">err</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">WriteFile</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">data</span><span class="p">,</span> <span class="mo">0644</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
 <span class="k">return</span> <span class="nx">err</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>如果需要在 if 之外使用函数调用的结果，则不应尝试缩小范围。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">if</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadFile</span><span class="p">(</span><span class="nx">name</span><span class="p">);</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
  <span class="nx">err</span> <span class="p">=</span> <span class="nx">cfg</span><span class="p">.</span><span class="nf">Decode</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
  <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">err</span>
  <span class="p">}</span>

  <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">cfg</span><span class="p">)</span>
  <span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">err</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">data</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadFile</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
   <span class="k">return</span> <span class="nx">err</span>
<span class="p">}</span>

<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cfg</span><span class="p">.</span><span class="nf">Decode</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">err</span>
<span class="p">}</span>

<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">cfg</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">nil</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="避免参数语义不明确avoid-naked-parameters">避免参数语义不明确(Avoid Naked Parameters)</h3>
<p>函数调用中的<code>意义不明确的参数</code>可能会损害可读性。当参数名称的含义不明显时，请为参数添加 C 样式注释 (<code>/* ... */</code>)</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// func printInfo(name string, isLocal, done bool)
</span><span class="c1"></span>
<span class="nf">printInfo</span><span class="p">(</span><span class="s">&#34;foo&#34;</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// func printInfo(name string, isLocal, done bool)
</span><span class="c1"></span>
<span class="nf">printInfo</span><span class="p">(</span><span class="s">&#34;foo&#34;</span><span class="p">,</span> <span class="kc">true</span> <span class="cm">/* isLocal */</span><span class="p">,</span> <span class="kc">true</span> <span class="cm">/* done */</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>对于上面的示例代码，还有一种更好的处理方式是将上面的 <code>bool</code> 类型换成自定义类型。将来，该参数可以支持不仅仅局限于两个状态（true/false）。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Region</span> <span class="kt">int</span>

<span class="kd">const</span> <span class="p">(</span>
  <span class="nx">UnknownRegion</span> <span class="nx">Region</span> <span class="p">=</span> <span class="kc">iota</span>
  <span class="nx">Local</span>
<span class="p">)</span>

<span class="kd">type</span> <span class="nx">Status</span> <span class="kt">int</span>

<span class="kd">const</span> <span class="p">(</span>
  <span class="nx">StatusReady</span> <span class="nx">Status</span><span class="p">=</span> <span class="kc">iota</span> <span class="o">+</span> <span class="mi">1</span>
  <span class="nx">StatusDone</span>
  <span class="c1">// Maybe we will have a StatusInProgress in the future.
</span><span class="c1"></span><span class="p">)</span>

<span class="kd">func</span> <span class="nf">printInfo</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">region</span> <span class="nx">Region</span><span class="p">,</span> <span class="nx">status</span> <span class="nx">Status</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><h3 id="使用原始字符串字面值避免转义">使用原始字符串字面值，避免转义</h3>
<p>Go 支持使用 <a href="https://golang.org/ref/spec#raw_string_lit">原始字符串字面值</a>，也就是 &quot; ` &quot; 来表示原生字符串，在需要转义的场景下，我们应该尽量使用这种方案来替换。</p>
<p>可以跨越多行并包含引号。使用这些字符串可以避免更难阅读的手工转义的字符串。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">wantError</span> <span class="o">:=</span> <span class="s">&#34;unknown name:\&#34;test\&#34;&#34;</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">wantError</span> <span class="o">:=</span> <span class="s">`unknown error:&#34;test&#34;`</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="初始化-struct-引用">初始化 Struct 引用</h3>
<p>在初始化结构引用时，请使用<code>&amp;T{}</code>代替<code>new(T)</code>，以使其与结构体初始化一致。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">sval</span> <span class="o">:=</span> <span class="nx">T</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;foo&#34;</span><span class="p">}</span>

<span class="c1">// inconsistent
</span><span class="c1"></span><span class="nx">sptr</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">T</span><span class="p">)</span>
<span class="nx">sptr</span><span class="p">.</span><span class="nx">Name</span> <span class="p">=</span> <span class="s">&#34;bar&#34;</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">sval</span> <span class="o">:=</span> <span class="nx">T</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;foo&#34;</span><span class="p">}</span>

<span class="nx">sptr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">T</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;bar&#34;</span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="初始化-maps">初始化 Maps</h3>
<p>对于空 map 请使用 <code>make(..)</code> 初始化， 并且 map 是通过编程方式填充的。
这使得 map 初始化在表现上不同于声明，并且它还可以方便地在 make 后添加大小提示。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="p">(</span>
  <span class="c1">// m1 读写安全;
</span><span class="c1"></span>  <span class="c1">// m2 在写入时会 panic
</span><span class="c1"></span>  <span class="nx">m1</span> <span class="p">=</span> <span class="kd">map</span><span class="p">[</span><span class="nx">T1</span><span class="p">]</span><span class="nx">T2</span><span class="p">{}</span>
  <span class="nx">m2</span> <span class="kd">map</span><span class="p">[</span><span class="nx">T1</span><span class="p">]</span><span class="nx">T2</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="p">(</span>
  <span class="c1">// m1 读写安全;
</span><span class="c1"></span>  <span class="c1">// m2 在写入时会 panic
</span><span class="c1"></span>  <span class="nx">m1</span> <span class="p">=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="nx">T1</span><span class="p">]</span><span class="nx">T2</span><span class="p">)</span>
  <span class="nx">m2</span> <span class="kd">map</span><span class="p">[</span><span class="nx">T1</span><span class="p">]</span><span class="nx">T2</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<p>声明和初始化看起来非常相似的。</p>
</td><td>
<p>声明和初始化看起来差别非常大。</p>
</td></tr>
</tbody></table>
<p>在尽可能的情况下，请在初始化时提供 map 容量大小，详细请看 <a href="#%E6%8C%87%E5%AE%9AMap%E5%AE%B9%E9%87%8F%E6%8F%90%E7%A4%BA">指定Map容量提示</a>。</p>
<p>另外，如果 map 包含固定的元素列表，则使用 map literals(map 初始化列表) 初始化映射。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">m</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="nx">T1</span><span class="p">]</span><span class="nx">T2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="nx">m</span><span class="p">[</span><span class="nx">k1</span><span class="p">]</span> <span class="p">=</span> <span class="nx">v1</span>
<span class="nx">m</span><span class="p">[</span><span class="nx">k2</span><span class="p">]</span> <span class="p">=</span> <span class="nx">v2</span>
<span class="nx">m</span><span class="p">[</span><span class="nx">k3</span><span class="p">]</span> <span class="p">=</span> <span class="nx">v3</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">m</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="nx">T1</span><span class="p">]</span><span class="nx">T2</span><span class="p">{</span>
  <span class="nx">k1</span><span class="p">:</span> <span class="nx">v1</span><span class="p">,</span>
  <span class="nx">k2</span><span class="p">:</span> <span class="nx">v2</span><span class="p">,</span>
  <span class="nx">k3</span><span class="p">:</span> <span class="nx">v3</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>基本准则是：在初始化时使用 map 初始化列表 来添加一组固定的元素。否则使用 <code>make</code> (如果可以，请尽量指定 map 容量)。</p>
<h3 id="字符串-string-format">字符串 string format</h3>
<p>如果你在函数外声明<code>Printf</code>-style 函数的格式字符串，请将其设置为<code>const</code>常量。</p>
<p>这有助于<code>go vet</code>对格式字符串执行静态分析。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">msg</span> <span class="o">:=</span> <span class="s">&#34;unexpected values %v, %v\n&#34;</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="nx">msg</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">const</span> <span class="nx">msg</span> <span class="p">=</span> <span class="s">&#34;unexpected values %v, %v\n&#34;</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="nx">msg</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="命名-printf-样式的函数">命名 Printf 样式的函数</h3>
<p>声明<code>Printf</code>-style 函数时，请确保<code>go vet</code>可以检测到它并检查格式字符串。</p>
<p>这意味着您应尽可能使用预定义的<code>Printf</code>-style 函数名称。<code>go vet</code>将默认检查这些。有关更多信息，请参见 <a href="https://golang.org/cmd/vet/#hdr-Printf_family">Printf 系列</a>。</p>
<p>如果不能使用预定义的名称，请以 f 结束选择的名称：<code>Wrapf</code>，而不是<code>Wrap</code>。<code>go vet</code>可以要求检查特定的 Printf 样式名称，但名称必须以<code>f</code>结尾。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell">$ go vet -printfuncs<span class="o">=</span>wrapf,statusf
</code></pre></td></tr></table>
</div>
</div><p>另请参阅 <a href="https://kuzminva.wordpress.com/2017/11/07/go-vet-printf-family-check/">go vet: Printf family check</a>.</p>
<h2 id="指导原则">指导原则</h2>
<h3 id="指向-interface-的指针">指向 interface 的指针</h3>
<p>您几乎不需要指向接口类型的指针。您应该将接口作为值进行传递，在这样的传递过程中，实质上传递的底层数据仍然可以是指针。</p>
<p>接口实质上在底层用两个字段表示：</p>
<ol>
<li>一个指向某些特定类型信息的指针。您可以将其视为&quot;type&quot;。</li>
<li>数据指针。如果存储的数据是指针，则直接存储。如果存储的数据是一个值，则存储指向该值的指针。</li>
</ol>
<p>如果希望接口方法修改基础数据，则必须使用指针传递(将对象指针赋值给接口变量)。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">F</span> <span class="kd">interface</span> <span class="p">{</span>
  <span class="nf">f</span><span class="p">()</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">S1</span> <span class="kd">struct</span><span class="p">{}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="nx">S1</span><span class="p">)</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>

<span class="kd">type</span> <span class="nx">S2</span> <span class="kd">struct</span><span class="p">{}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">S2</span><span class="p">)</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>

<span class="c1">// f1.f()无法修改底层数据
</span><span class="c1">// f2.f() 可以修改底层数据,给接口变量f2赋值时使用的是对象指针
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">f1</span> <span class="nx">F</span><span class="o">:=</span> <span class="nx">S1</span><span class="p">{}</span>
<span class="kd">var</span> <span class="nx">f2</span> <span class="nx">F</span><span class="o">:=</span> <span class="o">&amp;</span><span class="nx">S2</span><span class="p">{}</span>
</code></pre></td></tr></table>
</div>
</div><h3 id="interface-合理性验证">Interface 合理性验证</h3>
<p>在编译时验证接口的符合性。这包括：</p>
<ul>
<li>将实现特定接口的导出类型作为接口API 的一部分进行检查</li>
<li>实现同一接口的(导出和非导出)类型属于实现类型的集合</li>
<li>任何违反接口合理性检查的场景,都会终止编译,并通知给用户</li>
</ul>
<p>补充:上面3条是编译器对接口的检查机制,
大体意思是错误使用接口会在编译期报错.
所以可以利用这个机制让部分问题在编译期暴露.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// 如果Hnalder没有实现http.Handler,会在运行时报错
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">Handler</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
<span class="kd">func</span> <span class="p">(</span><span class="nx">h</span> <span class="o">*</span><span class="nx">Handler</span><span class="p">)</span> <span class="nf">ServeHTTP</span><span class="p">(</span>
  <span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span>
  <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span>
<span class="p">)</span> <span class="p">{</span>
  <span class="o">...</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Handler</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
<span class="c1">// 用于触发编译期的接口的合理性检查机制
</span><span class="c1">// 如果Hnalder没有实现http.Handler,会在编译期报错
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">_</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span> <span class="p">=</span> <span class="p">(</span><span class="o">*</span><span class="nx">Handler</span><span class="p">)(</span><span class="kc">nil</span><span class="p">)</span>
<span class="kd">func</span> <span class="p">(</span><span class="nx">h</span> <span class="o">*</span><span class="nx">Handler</span><span class="p">)</span> <span class="nf">ServeHTTP</span><span class="p">(</span>
  <span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span>
  <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span>
<span class="p">)</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>如果 <code>*Handler</code> 与 <code>http.Handler</code> 的接口不匹配,
那么语句 <code>var _ http.Handler = (*Handler)(nil)</code> 将无法编译通过.</p>
<p>赋值的右边应该是断言类型的零值。
对于指针类型（如 <code>*Handler</code>）、切片和映射，这是 <code>nil</code>；
对于结构类型，这是空结构。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">LogHandler</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">h</span>   <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span>
  <span class="nx">log</span> <span class="o">*</span><span class="nx">zap</span><span class="p">.</span><span class="nx">Logger</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">_</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span> <span class="p">=</span> <span class="nx">LogHandler</span><span class="p">{}</span>
<span class="kd">func</span> <span class="p">(</span><span class="nx">h</span> <span class="nx">LogHandler</span><span class="p">)</span> <span class="nf">ServeHTTP</span><span class="p">(</span>
  <span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span>
  <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span>
<span class="p">)</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h3 id="接收器-receiver-与接口">接收器 (receiver) 与接口</h3>
<p>使用值接收器的方法既可以通过值调用，也可以通过指针调用。</p>
<p>带指针接收器的方法只能通过指针或 <a href="https://golang.org/ref/spec#Method_values">addressable values</a>调用.</p>
<p>例如，</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">S</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">data</span> <span class="kt">string</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="nx">S</span><span class="p">)</span> <span class="nf">Read</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">s</span><span class="p">.</span><span class="nx">data</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">S</span><span class="p">)</span> <span class="nf">Write</span><span class="p">(</span><span class="nx">str</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">s</span><span class="p">.</span><span class="nx">data</span> <span class="p">=</span> <span class="nx">str</span>
<span class="p">}</span>

<span class="nx">sVals</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">int</span><span class="p">]</span><span class="nx">S</span><span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="p">{</span><span class="s">&#34;A&#34;</span><span class="p">}}</span>

<span class="c1">// 你只能通过值调用 Read
</span><span class="c1"></span><span class="nx">sVals</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">Read</span><span class="p">()</span>

<span class="c1">// 这不能编译通过：
</span><span class="c1">//  sVals[1].Write(&#34;test&#34;)
</span><span class="c1"></span>
<span class="nx">sPtrs</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">int</span><span class="p">]</span><span class="o">*</span><span class="nx">S</span><span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="p">{</span><span class="s">&#34;A&#34;</span><span class="p">}}</span>

<span class="c1">// 通过指针既可以调用 Read，也可以调用 Write 方法
</span><span class="c1"></span><span class="nx">sPtrs</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">Read</span><span class="p">()</span>
<span class="nx">sPtrs</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">Write</span><span class="p">(</span><span class="s">&#34;test&#34;</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p>类似的,即使方法有了值接收器,也同样可以用指针接收器来满足接口.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">F</span> <span class="kd">interface</span> <span class="p">{</span>
  <span class="nf">f</span><span class="p">()</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">S1</span> <span class="kd">struct</span><span class="p">{}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="nx">S1</span><span class="p">)</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>

<span class="kd">type</span> <span class="nx">S2</span> <span class="kd">struct</span><span class="p">{}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">S2</span><span class="p">)</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>

<span class="nx">s1Val</span> <span class="o">:=</span> <span class="nx">S1</span><span class="p">{}</span>
<span class="nx">s1Ptr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">S1</span><span class="p">{}</span>
<span class="nx">s2Val</span> <span class="o">:=</span> <span class="nx">S2</span><span class="p">{}</span>
<span class="nx">s2Ptr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">S2</span><span class="p">{}</span>

<span class="kd">var</span> <span class="nx">i</span> <span class="nx">F</span>
<span class="nx">i</span> <span class="p">=</span> <span class="nx">s1Val</span>
<span class="nx">i</span> <span class="p">=</span> <span class="nx">s1Ptr</span>
<span class="nx">i</span> <span class="p">=</span> <span class="nx">s2Ptr</span>

<span class="c1">//  下面代码无法通过编译。因为 s2Val 是一个值，而 S2 的 f 方法中没有使用值接收器
</span><span class="c1">//   i = s2Val
</span></code></pre></td></tr></table>
</div>
</div><p><a href="https://golang.org/doc/effective_go.html">Effective Go</a> 中有一段关于 <a href="https://golang.org/doc/effective_go.html#pointers_vs_values">pointers vs. values</a> 的精彩讲解。</p>
<p>补充:</p>
<ul>
<li>一个类型可以有值接收器方法集和指针接收器方法集
<ul>
<li>值接收器方法集是指针接收器方法集的子集,反之不是</li>
</ul>
</li>
<li>规则
<ul>
<li>值对象只可以使用值接收器方法集</li>
<li>指针对象可以使用 值接收器方法集 + 指针接收器方法集</li>
</ul>
</li>
<li>接口的匹配(或者叫实现)
<ul>
<li>类型实现了接口的所有方法,叫匹配</li>
<li>具体的讲,要么是类型的值方法集匹配接口,要么是指针方法集匹配接口</li>
</ul>
</li>
</ul>
<p>具体的匹配分两种:</p>
<ul>
<li>值方法集和接口匹配
<ul>
<li>给接口变量赋值的不管是值还是指针对象,都ok,因为都包含值方法集</li>
</ul>
</li>
<li>指针方法集和接口匹配
<ul>
<li>只能将指针对象赋值给接口变量,因为只有指针方法集和接口匹配</li>
<li>如果将值对象赋值给接口变量,会在编译期报错(会触发接口合理性检查机制)</li>
</ul>
</li>
</ul>
<p>为啥 i = s2Val 会报错,因为值方法集和接口不匹配.</p>
<h3 id="零值-mutex-是有效的">零值 Mutex 是有效的</h3>
<p>零值 <code>sync.Mutex</code> 和 <code>sync.RWMutex</code> 是有效的。所以指向 mutex 的指针基本是不必要的。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">mu</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span><span class="p">)</span>
<span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">mu</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>
<span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>如果你使用结构体指针，mutex 可以非指针形式作为结构体的组成字段，或者更好的方式是直接嵌入到结构体中。
如果是私有结构体类型或是要实现 Mutex 接口的类型，我们可以使用嵌入 mutex 的方法：</p>
<table>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">smap</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span> <span class="c1">// only for unexported types（仅适用于非导出类型）
</span><span class="c1"></span>
  <span class="nx">data</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">newSMap</span><span class="p">()</span> <span class="o">*</span><span class="nx">smap</span> <span class="p">{</span>
  <span class="k">return</span> <span class="o">&amp;</span><span class="nx">smap</span><span class="p">{</span>
    <span class="nx">data</span><span class="p">:</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">),</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">smap</span><span class="p">)</span> <span class="nf">Get</span><span class="p">(</span><span class="nx">k</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
  <span class="nx">m</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
  <span class="k">defer</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>

  <span class="k">return</span> <span class="nx">m</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">SMap</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">mu</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span> <span class="c1">// 对于导出类型，请使用私有锁
</span><span class="c1"></span>
  <span class="nx">data</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">NewSMap</span><span class="p">()</span> <span class="o">*</span><span class="nx">SMap</span> <span class="p">{</span>
  <span class="k">return</span> <span class="o">&amp;</span><span class="nx">SMap</span><span class="p">{</span>
    <span class="nx">data</span><span class="p">:</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">),</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">SMap</span><span class="p">)</span> <span class="nf">Get</span><span class="p">(</span><span class="nx">k</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
  <span class="nx">m</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
  <span class="k">defer</span> <span class="nx">m</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>

  <span class="k">return</span> <span class="nx">m</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tr>
<tr>
<td>为私有类型或需要实现互斥接口的类型嵌入。</td>
<td>对于导出的类型，请使用专用字段。</td>
</tr>
</tbody></table>
<h3 id="在边界处拷贝-slices-和-maps">在边界处拷贝 Slices 和 Maps</h3>
<p>slices 和 maps 包含了指向底层数据的指针，因此在需要复制它们时要特别注意。</p>
<h4 id="接收-slices-和-maps">接收 Slices 和 Maps</h4>
<p>请记住，当 map 或 slice 作为函数参数传入时，如果您存储了对它们的引用，则用户可以对其进行修改。</p>
<table>
<thead><tr><th>Bad</th> <th>Good</th></tr></thead>
<tbody>
<tr>
<td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="p">(</span><span class="nx">d</span> <span class="o">*</span><span class="nx">Driver</span><span class="p">)</span> <span class="nf">SetTrips</span><span class="p">(</span><span class="nx">trips</span> <span class="p">[]</span><span class="nx">Trip</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">d</span><span class="p">.</span><span class="nx">trips</span> <span class="p">=</span> <span class="nx">trips</span>
<span class="p">}</span>

<span class="nx">trips</span> <span class="o">:=</span> <span class="o">...</span>
<span class="nx">d1</span><span class="p">.</span><span class="nf">SetTrips</span><span class="p">(</span><span class="nx">trips</span><span class="p">)</span>

<span class="c1">// 你是要修改 d1.trips 吗？
</span><span class="c1"></span><span class="nx">trips</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">=</span> <span class="o">...</span>
</code></pre></td></tr></table>
</div>
</div></td>
<td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="p">(</span><span class="nx">d</span> <span class="o">*</span><span class="nx">Driver</span><span class="p">)</span> <span class="nf">SetTrips</span><span class="p">(</span><span class="nx">trips</span> <span class="p">[]</span><span class="nx">Trip</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">d</span><span class="p">.</span><span class="nx">trips</span> <span class="p">=</span> <span class="nb">make</span><span class="p">([]</span><span class="nx">Trip</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">trips</span><span class="p">))</span>
  <span class="nb">copy</span><span class="p">(</span><span class="nx">d</span><span class="p">.</span><span class="nx">trips</span><span class="p">,</span> <span class="nx">trips</span><span class="p">)</span>
<span class="p">}</span>

<span class="nx">trips</span> <span class="o">:=</span> <span class="o">...</span>
<span class="nx">d1</span><span class="p">.</span><span class="nf">SetTrips</span><span class="p">(</span><span class="nx">trips</span><span class="p">)</span>

<span class="c1">// 这里我们修改 trips[0]，但不会影响到 d1.trips
</span><span class="c1"></span><span class="nx">trips</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">=</span> <span class="o">...</span>
</code></pre></td></tr></table>
</div>
</div></td>
</tr>
</tbody>
</table>
<h4 id="返回-slices-或-maps">返回 slices 或 maps</h4>
<p>同样，请注意用户对暴露内部状态的 map 或 slice 的修改。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Stats</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">mu</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>

  <span class="nx">counters</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span>
<span class="p">}</span>

<span class="c1">// Snapshot 返回当前状态。
</span><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Stats</span><span class="p">)</span> <span class="nf">Snapshot</span><span class="p">()</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span> <span class="p">{</span>
  <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
  <span class="k">defer</span> <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>

  <span class="k">return</span> <span class="nx">s</span><span class="p">.</span><span class="nx">counters</span>
<span class="p">}</span>

<span class="c1">// snapshot 不再受互斥锁保护
</span><span class="c1">// 因此对 snapshot 的任何访问都将受到数据竞争的影响
</span><span class="c1">// 影响 stats.counters
</span><span class="c1"></span><span class="nx">snapshot</span> <span class="o">:=</span> <span class="nx">stats</span><span class="p">.</span><span class="nf">Snapshot</span><span class="p">()</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Stats</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">mu</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>

  <span class="nx">counters</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Stats</span><span class="p">)</span> <span class="nf">Snapshot</span><span class="p">()</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span> <span class="p">{</span>
  <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
  <span class="k">defer</span> <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>

  <span class="nx">result</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nx">counters</span><span class="p">))</span>
  <span class="k">for</span> <span class="nx">k</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">s</span><span class="p">.</span><span class="nx">counters</span> <span class="p">{</span>
    <span class="nx">result</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="p">=</span> <span class="nx">v</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">result</span>
<span class="p">}</span>

<span class="c1">// snapshot 现在是一个拷贝
</span><span class="c1"></span><span class="nx">snapshot</span> <span class="o">:=</span> <span class="nx">stats</span><span class="p">.</span><span class="nf">Snapshot</span><span class="p">()</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="使用-defer-释放资源">使用 defer 释放资源</h3>
<p>使用 defer 释放资源，诸如文件和锁。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">p</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
<span class="k">if</span> <span class="nx">p</span><span class="p">.</span><span class="nx">count</span> <span class="p">&lt;</span> <span class="mi">10</span> <span class="p">{</span>
  <span class="nx">p</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
  <span class="k">return</span> <span class="nx">p</span><span class="p">.</span><span class="nx">count</span>
<span class="p">}</span>

<span class="nx">p</span><span class="p">.</span><span class="nx">count</span><span class="o">++</span>
<span class="nx">newCount</span> <span class="o">:=</span> <span class="nx">p</span><span class="p">.</span><span class="nx">count</span>
<span class="nx">p</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>

<span class="k">return</span> <span class="nx">newCount</span>

<span class="c1">// 当有多个 return 分支时，很容易遗忘 unlock
</span></code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">p</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
<span class="k">defer</span> <span class="nx">p</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>

<span class="k">if</span> <span class="nx">p</span><span class="p">.</span><span class="nx">count</span> <span class="p">&lt;</span> <span class="mi">10</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">p</span><span class="p">.</span><span class="nx">count</span>
<span class="p">}</span>

<span class="nx">p</span><span class="p">.</span><span class="nx">count</span><span class="o">++</span>
<span class="k">return</span> <span class="nx">p</span><span class="p">.</span><span class="nx">count</span>

<span class="c1">// 更可读
</span></code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>Defer 的开销非常小，只有在您可以证明函数执行时间处于纳秒级的程度时，才应避免这样做。使用 defer 提升可读性是值得的，因为使用它们的成本微不足道。尤其适用于那些不仅仅是简单内存访问的较大的方法，在这些方法中其他计算的资源消耗远超过 <code>defer</code>。</p>
<h3 id="channel-的-size-要么是-1要么是无缓冲的">Channel 的 size 要么是 1，要么是无缓冲的</h3>
<p>channel 通常 size 应为 1 或是无缓冲的。默认情况下，channel 是无缓冲的，其 size 为零。任何其他尺寸都必须经过严格的审查。我们需要考虑如何确定大小，考虑是什么阻止了 channel 在高负载下和阻塞写时的写入，以及当这种情况发生时系统逻辑有哪些变化。(翻译解释：按照原文意思是需要界定通道边界，竞态条件，以及逻辑上下文梳理)</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// 应该足以满足任何情况！
</span><span class="c1"></span><span class="nx">c</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">,</span> <span class="mi">64</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// 大小：1
</span><span class="c1"></span><span class="nx">c</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">// 或者
</span><span class="c1">// 无缓冲 channel，大小为 0
</span><span class="c1"></span><span class="nx">c</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="枚举从-1-开始">枚举从 1 开始</h3>
<p>在 Go 中引入枚举的标准方法是声明一个自定义类型和一个使用了 iota 的 const 组。由于变量的默认值为 0，因此通常应以非零值开头枚举。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Operation</span> <span class="kt">int</span>

<span class="kd">const</span> <span class="p">(</span>
  <span class="nx">Add</span> <span class="nx">Operation</span> <span class="p">=</span> <span class="kc">iota</span>
  <span class="nx">Subtract</span>
  <span class="nx">Multiply</span>
<span class="p">)</span>

<span class="c1">// Add=0, Subtract=1, Multiply=2
</span></code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Operation</span> <span class="kt">int</span>

<span class="kd">const</span> <span class="p">(</span>
  <span class="nx">Add</span> <span class="nx">Operation</span> <span class="p">=</span> <span class="kc">iota</span> <span class="o">+</span> <span class="mi">1</span>
  <span class="nx">Subtract</span>
  <span class="nx">Multiply</span>
<span class="p">)</span>

<span class="c1">// Add=1, Subtract=2, Multiply=3
</span></code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>在某些情况下，使用零值是有意义的（枚举从零开始），例如，当零值是理想的默认行为时。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">LogOutput</span> <span class="kt">int</span>

<span class="kd">const</span> <span class="p">(</span>
  <span class="nx">LogToStdout</span> <span class="nx">LogOutput</span> <span class="p">=</span> <span class="kc">iota</span>
  <span class="nx">LogToFile</span>
  <span class="nx">LogToRemote</span>
<span class="p">)</span>

<span class="c1">// LogToStdout=0, LogToFile=1, LogToRemote=2
</span></code></pre></td></tr></table>
</div>
</div><h3 id="使用-time-处理时间">使用 time 处理时间</h3>
<p>时间处理很复杂。关于时间的错误假设通常包括以下几点。</p>
<ol>
<li>一天有 24 小时</li>
<li>一小时有 60 分钟</li>
<li>一周有七天</li>
<li>一年 365 天</li>
<li><a href="https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time">还有更多</a></li>
</ol>
<p>例如，<em>1</em> 表示在一个时间点上加上 24 小时并不总是产生一个新的日历日。</p>
<p>因此，在处理时间时始终使用 <a href="https://golang.org/pkg/time/"><code>&quot;time&quot;</code></a> 包，因为它有助于以更安全、更准确的方式处理这些不正确的假设。</p>
<h4 id="使用-timetime-表达瞬时时间">使用 <code>time.Time</code> 表达瞬时时间</h4>
<p>在处理时间的瞬间时使用 <a href="https://golang.org/pkg/time/#Time"><code>time.time</code></a>，在比较、添加或减去时间时使用 <code>time.Time</code> 中的方法。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">isActive</span><span class="p">(</span><span class="nx">now</span><span class="p">,</span> <span class="nx">start</span><span class="p">,</span> <span class="nx">stop</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">start</span> <span class="o">&lt;=</span> <span class="nx">now</span> <span class="o">&amp;&amp;</span> <span class="nx">now</span> <span class="p">&lt;</span> <span class="nx">stop</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">isActive</span><span class="p">(</span><span class="nx">now</span><span class="p">,</span> <span class="nx">start</span><span class="p">,</span> <span class="nx">stop</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span><span class="nx">start</span><span class="p">.</span><span class="nf">Before</span><span class="p">(</span><span class="nx">now</span><span class="p">)</span> <span class="o">||</span> <span class="nx">start</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">now</span><span class="p">))</span> <span class="o">&amp;&amp;</span> <span class="nx">now</span><span class="p">.</span><span class="nf">Before</span><span class="p">(</span><span class="nx">stop</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h4 id="使用-timeduration-表达时间段">使用 <code>time.Duration</code> 表达时间段</h4>
<p>在处理时间段时使用 <a href="https://golang.org/pkg/time/#Duration"><code>time.Duration</code></a> .</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">poll</span><span class="p">(</span><span class="nx">delay</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">{</span>
    <span class="c1">// ...
</span><span class="c1"></span>    <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Duration</span><span class="p">(</span><span class="nx">delay</span><span class="p">)</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="nf">poll</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="c1">// 是几秒钟还是几毫秒?
</span></code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">poll</span><span class="p">(</span><span class="nx">delay</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">{</span>
    <span class="c1">// ...
</span><span class="c1"></span>    <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="nx">delay</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="nf">poll</span><span class="p">(</span><span class="mi">10</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>回到第一个例子，在一个时间瞬间加上 24 小时，我们用于添加时间的方法取决于意图。如果我们想要下一个日历日(当前天的下一天)的同一个时间点，我们应该使用 <a href="https://golang.org/pkg/time/#Time.AddDate"><code>Time.AddDate</code></a>。但是，如果我们想保证某一时刻比前一时刻晚 24 小时，我们应该使用 <a href="https://golang.org/pkg/time/#Time.Add"><code>Time.Add</code></a>。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">newDay</span> <span class="o">:=</span> <span class="nx">t</span><span class="p">.</span><span class="nf">AddDate</span><span class="p">(</span><span class="mi">0</span> <span class="cm">/* years */</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="cm">/* months */</span><span class="p">,</span> <span class="mi">1</span> <span class="cm">/* days */</span><span class="p">)</span>
<span class="nx">maybeNewDay</span> <span class="o">:=</span> <span class="nx">t</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">24</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Hour</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="对外部系统使用-timetime-和-timeduration">对外部系统使用 <code>time.Time</code> 和 <code>time.Duration</code></h4>
<p>尽可能在与外部系统的交互中使用 <code>time.Duration</code> 和 <code>time.Time</code> 例如 :</p>
<ul>
<li>
<p>Command-line 标志: <a href="https://golang.org/pkg/flag/"><code>flag</code></a> 通过 <a href="https://golang.org/pkg/time/#ParseDuration"><code>time.ParseDuration</code></a> 支持 <code>time.Duration</code></p>
</li>
<li>
<p>JSON: <a href="https://golang.org/pkg/encoding/json/"><code>encoding/json</code></a> 通过其 <a href="https://golang.org/pkg/time/#Time.UnmarshalJSON"><code>UnmarshalJSON</code> method</a> 方法支持将 <code>time.Time</code> 编码为 <a href="https://tools.ietf.org/html/rfc3339">RFC 3339</a> 字符串</p>
</li>
<li>
<p>SQL: <a href="https://golang.org/pkg/database/sql/"><code>database/sql</code></a> 支持将 <code>DATETIME</code> 或 <code>TIMESTAMP</code> 列转换为 <code>time.Time</code>，如果底层驱动程序支持则返回</p>
</li>
<li>
<p>YAML: <a href="https://godoc.org/gopkg.in/yaml.v2"><code>gopkg.in/yaml.v2</code></a> 支持将 <code>time.Time</code> 作为 <a href="https://tools.ietf.org/html/rfc3339">RFC 3339</a> 字符串，并通过 <a href="https://golang.org/pkg/time/#ParseDuration"><code>time.ParseDuration</code></a> 支持 <code>time.Duration</code>。</p>
</li>
</ul>
<p>当不能在这些交互中使用 <code>time.Duration</code> 时，请使用 <code>int</code> 或 <code>float64</code>，并在字段名称中包含单位。</p>
<p>例如，由于 <code>encoding/json</code> 不支持 <code>time.Duration</code>，因此该单位包含在字段的名称中。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// {&#34;interval&#34;: 2}
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">Config</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">Interval</span> <span class="kt">int</span> <span class="s">`json:&#34;interval&#34;`</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// {&#34;intervalMillis&#34;: 2000}
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">Config</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">IntervalMillis</span> <span class="kt">int</span> <span class="s">`json:&#34;intervalMillis&#34;`</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>当在这些交互中不能使用 <code>time.Time</code> 时，除非达成一致，否则使用 <code>string</code> 和 <a href="https://tools.ietf.org/html/rfc3339">RFC 3339</a> 中定义的格式时间戳。默认情况下，<a href="https://golang.org/pkg/time/#Time.UnmarshalText"><code>Time.UnmarshalText</code></a> 使用此格式，并可通过 <a href="https://golang.org/pkg/time/#RFC3339"><code>time.RFC3339</code></a> 在 <code>Time.Format</code> 和 <code>time.Parse</code> 中使用。</p>
<p>尽管这在实践中并不成问题，但请记住，<code>&quot;time&quot;</code> 包不支持解析闰秒时间戳（<a href="https://github.com/golang/go/issues/8728">8728</a>），也不在计算中考虑闰秒（<a href="https://github.com/golang/go/issues/15190">15190</a>）。如果您比较两个时间瞬间，则差异将不包括这两个瞬间之间可能发生的闰秒。</p>
<!-- TODO: section on String methods for enums -->
<h3 id="错误类型">错误类型</h3>
<p>Go 中有多种声明错误（Error) 的选项：</p>
<ul>
<li><a href="https://golang.org/pkg/errors/#New"><code>errors.New</code></a> 对于简单静态字符串的错误</li>
<li><a href="https://golang.org/pkg/fmt/#Errorf"><code>fmt.Errorf</code></a> 用于格式化的错误字符串</li>
<li>实现 <code>Error()</code> 方法的自定义类型</li>
<li>用 <a href="https://godoc.org/github.com/pkg/errors#Wrap"><code>&quot;pkg/errors&quot;.Wrap</code></a> 的 Wrapped errors</li>
</ul>
<p>返回错误时，请考虑以下因素以确定最佳选择：</p>
<ul>
<li>
<p>这是一个不需要额外信息的简单错误吗？如果是这样，<a href="https://golang.org/pkg/errors/#New"><code>errors.New</code></a> 足够了。</p>
</li>
<li>
<p>客户需要检测并处理此错误吗？如果是这样，则应使用自定义类型并实现该 <code>Error()</code> 方法。</p>
</li>
<li>
<p>您是否正在传播下游函数返回的错误？如果是这样，请查看本文后面有关错误包装 <a href="#%E9%94%99%E8%AF%AF%E5%8C%85%E8%A3%85" title="Error-Wrapping">section on error wrapping</a> 部分的内容。</p>
</li>
<li>
<p>否则 <a href="https://golang.org/pkg/fmt/#Errorf"><code>fmt.Errorf</code></a> 就可以了。</p>
</li>
</ul>
<p>如果客户端需要检测错误，并且您已使用创建了一个简单的错误 <a href="https://golang.org/pkg/errors/#New"><code>errors.New</code></a>，请使用一个错误变量。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// package foo
</span><span class="c1"></span>
<span class="kd">func</span> <span class="nf">Open</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;could not open&#34;</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// package bar
</span><span class="c1"></span>
<span class="kd">func</span> <span class="nf">use</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">foo</span><span class="p">.</span><span class="nf">Open</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nx">err</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;could not open&#34;</span> <span class="p">{</span>
      <span class="c1">// handle
</span><span class="c1"></span>    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;unknown error&#34;</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// package foo
</span><span class="c1"></span>
<span class="kd">var</span> <span class="nx">ErrCouldNotOpen</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;could not open&#34;</span><span class="p">)</span>

<span class="kd">func</span> <span class="nf">Open</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">ErrCouldNotOpen</span>
<span class="p">}</span>

<span class="c1">// package bar
</span><span class="c1"></span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">foo</span><span class="p">.</span><span class="nf">Open</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nx">err</span> <span class="o">==</span> <span class="nx">foo</span><span class="p">.</span><span class="nx">ErrCouldNotOpen</span> <span class="p">{</span>
    <span class="c1">// handle
</span><span class="c1"></span>  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;unknown error&#34;</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>如果您有可能需要客户端检测的错误，并且想向其中添加更多信息（例如，它不是静态字符串），则应使用自定义类型。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">open</span><span class="p">(</span><span class="nx">file</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;file %q not found&#34;</span><span class="p">,</span> <span class="nx">file</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">use</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">open</span><span class="p">(</span><span class="s">&#34;testfile.txt&#34;</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">err</span><span class="p">.</span><span class="nf">Error</span><span class="p">(),</span> <span class="s">&#34;not found&#34;</span><span class="p">)</span> <span class="p">{</span>
      <span class="c1">// handle
</span><span class="c1"></span>    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;unknown error&#34;</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">errNotFound</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">file</span> <span class="kt">string</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="nx">errNotFound</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;file %q not found&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nx">file</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">open</span><span class="p">(</span><span class="nx">file</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">errNotFound</span><span class="p">{</span><span class="nx">file</span><span class="p">:</span> <span class="nx">file</span><span class="p">}</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">use</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">open</span><span class="p">(</span><span class="s">&#34;testfile.txt&#34;</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">err</span><span class="p">.(</span><span class="nx">errNotFound</span><span class="p">);</span> <span class="nx">ok</span> <span class="p">{</span>
      <span class="c1">// handle
</span><span class="c1"></span>    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;unknown error&#34;</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>直接导出自定义错误类型时要小心，因为它们已成为程序包公共 API 的一部分。最好公开匹配器功能以检查错误。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// package foo
</span><span class="c1"></span>
<span class="kd">type</span> <span class="nx">errNotFound</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">file</span> <span class="kt">string</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="nx">errNotFound</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;file %q not found&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nx">file</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">IsNotFoundError</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
  <span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">err</span><span class="p">.(</span><span class="nx">errNotFound</span><span class="p">)</span>
  <span class="k">return</span> <span class="nx">ok</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">Open</span><span class="p">(</span><span class="nx">file</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">errNotFound</span><span class="p">{</span><span class="nx">file</span><span class="p">:</span> <span class="nx">file</span><span class="p">}</span>
<span class="p">}</span>

<span class="c1">// package bar
</span><span class="c1"></span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">foo</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="s">&#34;foo&#34;</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nx">foo</span><span class="p">.</span><span class="nf">IsNotFoundError</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// handle
</span><span class="c1"></span>  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;unknown error&#34;</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><!-- TODO: Exposing the information to callers with accessor functions. -->
<h3 id="错误包装-error-wrapping">错误包装 (Error Wrapping)</h3>
<p>一个（函数/方法）调用失败时，有三种主要的错误传播方式：</p>
<ul>
<li>如果没有要添加的其他上下文，并且您想要维护原始错误类型，则返回原始错误。</li>
<li>添加上下文，使用 <a href="https://godoc.org/github.com/pkg/errors#Wrap"><code>&quot;pkg/errors&quot;.Wrap</code></a> 以便错误消息提供更多上下文 ,<a href="https://godoc.org/github.com/pkg/errors#Cause"><code>&quot;pkg/errors&quot;.Cause</code></a> 可用于提取原始错误。</li>
<li>如果调用者不需要检测或处理的特定错误情况，使用 <a href="https://golang.org/pkg/fmt/#Errorf"><code>fmt.Errorf</code></a>。</li>
</ul>
<p>建议在可能的地方添加上下文，以使您获得诸如“调用服务 foo：连接被拒绝”之类的更有用的错误，而不是诸如“连接被拒绝”之类的模糊错误。</p>
<p>在将上下文添加到返回的错误时，请避免使用“failed to”之类的短语以保持上下文简洁，这些短语会陈述明显的内容，并随着错误在堆栈中的渗透而逐渐堆积：</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">s</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">store</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span>
        <span class="s">&#34;failed to create new store: %s&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">s</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">store</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span>
        <span class="s">&#34;new store: %s&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">failed to x: failed to y: failed to create new store: the error
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">x: y: new store: the error
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>但是，一旦将错误发送到另一个系统，就应该明确消息是错误消息（例如使用<code>err</code>标记，或在日志中以”Failed”为前缀）。</p>
<p>另请参见 <a href="https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully">Don&rsquo;t just check errors, handle them gracefully</a>. 不要只是检查错误，要优雅地处理错误</p>
<h3 id="处理类型断言失败">处理类型断言失败</h3>
<p><a href="https://golang.org/ref/spec#Type_assertions">type assertion</a> 的单个返回值形式针对不正确的类型将产生 panic。因此，请始终使用“comma ok”的惯用法。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">t</span> <span class="o">:=</span> <span class="nx">i</span><span class="p">.(</span><span class="kt">string</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">t</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">i</span><span class="p">.(</span><span class="kt">string</span><span class="p">)</span>
<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
  <span class="c1">// 优雅地处理错误
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<!-- TODO: There are a few situations where the single assignment form is
fine. -->
<h3 id="不要-panic">不要 panic</h3>
<p>在生产环境中运行的代码必须避免出现 panic。panic 是 <a href="https://en.wikipedia.org/wiki/Cascading_failure">cascading failures</a> 级联失败的主要根源 。如果发生错误，该函数必须返回错误，并允许调用方决定如何处理它。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nx">bar</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">bar</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
    <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;bar must not be empty&#34;</span><span class="p">)</span>
  <span class="p">}</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">2</span> <span class="p">{</span>
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;USAGE: foo &lt;bar&gt;&#34;</span><span class="p">)</span>
    <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">}</span>
  <span class="nf">foo</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nx">bar</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">bar</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;bar must not be empty&#34;</span><span class="p">)</span>
  <span class="p">}</span>
  <span class="c1">// ...
</span><span class="c1"></span>  <span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">2</span> <span class="p">{</span>
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;USAGE: foo &lt;bar&gt;&#34;</span><span class="p">)</span>
    <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">foo</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
    <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>panic/recover 不是错误处理策略。仅当发生不可恢复的事情（例如：nil 引用）时，程序才必须 panic。程序初始化是一个例外：程序启动时应使程序中止的不良情况可能会引起 panic。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">_statusTemplate</span> <span class="p">=</span> <span class="nx">template</span><span class="p">.</span><span class="nf">Must</span><span class="p">(</span><span class="nx">template</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">).</span><span class="nf">Parse</span><span class="p">(</span><span class="s">&#34;_statusHTML&#34;</span><span class="p">))</span>
</code></pre></td></tr></table>
</div>
</div><p>即使在测试代码中，也优先使用<code>t.Fatal</code>或者<code>t.FailNow</code>而不是 panic 来确保失败被标记。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// func TestFoo(t *testing.T)
</span><span class="c1"></span>
<span class="nx">f</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">TempFile</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">,</span> <span class="s">&#34;test&#34;</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
  <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;failed to set up test&#34;</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// func TestFoo(t *testing.T)
</span><span class="c1"></span>
<span class="nx">f</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">TempFile</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">,</span> <span class="s">&#34;test&#34;</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
  <span class="nx">t</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="s">&#34;failed to set up test&#34;</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<!-- TODO: Explain how to use _test packages. -->
<h3 id="使用-gouberorgatomic">使用 go.uber.org/atomic</h3>
<p>使用 <a href="https://golang.org/pkg/sync/atomic/">sync/atomic</a> 包的原子操作对原始类型 (<code>int32</code>, <code>int64</code>等）进行操作，因为很容易忘记使用原子操作来读取或修改变量。</p>
<p><a href="https://godoc.org/go.uber.org/atomic">go.uber.org/atomic</a> 通过隐藏基础类型为这些操作增加了类型安全性。此外，它包括一个方便的<code>atomic.Bool</code>类型。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">foo</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">running</span> <span class="kt">int32</span>  <span class="c1">// atomic
</span><span class="c1"></span><span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">f</span><span class="o">*</span> <span class="nx">foo</span><span class="p">)</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nx">atomic</span><span class="p">.</span><span class="nf">SwapInt32</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">f</span><span class="p">.</span><span class="nx">running</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span>
     <span class="c1">// already running…
</span><span class="c1"></span>     <span class="k">return</span>
  <span class="p">}</span>
  <span class="c1">// start the Foo
</span><span class="c1"></span><span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="o">*</span><span class="nx">foo</span><span class="p">)</span> <span class="nf">isRunning</span><span class="p">()</span> <span class="kt">bool</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">f</span><span class="p">.</span><span class="nx">running</span> <span class="o">==</span> <span class="mi">1</span>  <span class="c1">// race!
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">foo</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">running</span> <span class="nx">atomic</span><span class="p">.</span><span class="nx">Bool</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="o">*</span><span class="nx">foo</span><span class="p">)</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="nx">f</span><span class="p">.</span><span class="nx">running</span><span class="p">.</span><span class="nf">Swap</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
     <span class="c1">// already running…
</span><span class="c1"></span>     <span class="k">return</span>
  <span class="p">}</span>
  <span class="c1">// start the Foo
</span><span class="c1"></span><span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="o">*</span><span class="nx">foo</span><span class="p">)</span> <span class="nf">isRunning</span><span class="p">()</span> <span class="kt">bool</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">f</span><span class="p">.</span><span class="nx">running</span><span class="p">.</span><span class="nf">Load</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="避免可变全局变量">避免可变全局变量</h3>
<p>使用选择依赖注入方式避免改变全局变量。
既适用于函数指针又适用于其他值类型</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// sign.go
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">_timeNow</span> <span class="p">=</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Now</span>
<span class="kd">func</span> <span class="nf">sign</span><span class="p">(</span><span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
  <span class="nx">now</span> <span class="o">:=</span> <span class="nf">_timeNow</span><span class="p">()</span>
  <span class="k">return</span> <span class="nf">signWithTime</span><span class="p">(</span><span class="nx">msg</span><span class="p">,</span> <span class="nx">now</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// sign.go
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">signer</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">now</span> <span class="kd">func</span><span class="p">()</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">newSigner</span><span class="p">()</span> <span class="o">*</span><span class="nx">signer</span> <span class="p">{</span>
  <span class="k">return</span> <span class="o">&amp;</span><span class="nx">signer</span><span class="p">{</span>
    <span class="nx">now</span><span class="p">:</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">,</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">signer</span><span class="p">)</span> <span class="nf">Sign</span><span class="p">(</span><span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
  <span class="nx">now</span> <span class="o">:=</span> <span class="nx">s</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span>
  <span class="k">return</span> <span class="nf">signWithTime</span><span class="p">(</span><span class="nx">msg</span><span class="p">,</span> <span class="nx">now</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// sign_test.go
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">TestSign</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">oldTimeNow</span> <span class="o">:=</span> <span class="nx">_timeNow</span>
  <span class="nx">_timeNow</span> <span class="p">=</span> <span class="kd">func</span><span class="p">()</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">someFixedTime</span>
  <span class="p">}</span>
  <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> <span class="nx">_timeNow</span> <span class="p">=</span> <span class="nx">oldTimeNow</span> <span class="p">}()</span>
  <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">want</span><span class="p">,</span> <span class="nf">sign</span><span class="p">(</span><span class="nx">give</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// sign_test.go
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">TestSigner</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">s</span> <span class="o">:=</span> <span class="nf">newSigner</span><span class="p">()</span>
  <span class="nx">s</span><span class="p">.</span><span class="nx">now</span> <span class="p">=</span> <span class="kd">func</span><span class="p">()</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">someFixedTime</span>
  <span class="p">}</span>
  <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">want</span><span class="p">,</span> <span class="nx">s</span><span class="p">.</span><span class="nf">Sign</span><span class="p">(</span><span class="nx">give</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="避免在公共结构中嵌入类型">避免在公共结构中嵌入类型</h3>
<p>这些嵌入的类型泄漏实现细节、禁止类型演化和模糊的文档。</p>
<p>假设您使用共享的 <code>AbstractList</code> 实现了多种列表类型，请避免在具体的列表实现中嵌入 <code>AbstractList</code>。
相反，只需手动将方法写入具体的列表，该列表将委托给抽象列表。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">AbstractList</span> <span class="kd">struct</span> <span class="p">{}</span>
<span class="c1">// 添加将实体添加到列表中。
</span><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">l</span> <span class="o">*</span><span class="nx">AbstractList</span><span class="p">)</span> <span class="nf">Add</span><span class="p">(</span><span class="nx">e</span> <span class="nx">Entity</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
<span class="c1">// 移除从列表中移除实体。
</span><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">l</span> <span class="o">*</span><span class="nx">AbstractList</span><span class="p">)</span> <span class="nf">Remove</span><span class="p">(</span><span class="nx">e</span> <span class="nx">Entity</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// ConcreteList 是一个实体列表。
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">ConcreteList</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="o">*</span><span class="nx">AbstractList</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// ConcreteList 是一个实体列表。
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">ConcreteList</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">list</span> <span class="o">*</span><span class="nx">AbstractList</span>
<span class="p">}</span>
<span class="c1">// 添加将实体添加到列表中。
</span><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">l</span> <span class="o">*</span><span class="nx">ConcreteList</span><span class="p">)</span> <span class="nf">Add</span><span class="p">(</span><span class="nx">e</span> <span class="nx">Entity</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">l</span><span class="p">.</span><span class="nx">list</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 移除从列表中移除实体。
</span><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">l</span> <span class="o">*</span><span class="nx">ConcreteList</span><span class="p">)</span> <span class="nf">Remove</span><span class="p">(</span><span class="nx">e</span> <span class="nx">Entity</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">l</span><span class="p">.</span><span class="nx">list</span><span class="p">.</span><span class="nf">Remove</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>Go 允许 <a href="https://golang.org/doc/effective_go.html#embedding">类型嵌入</a> 作为继承和组合之间的折衷。
外部类型获取嵌入类型的方法的隐式副本。
默认情况下，这些方法委托给嵌入实例的同一方法。</p>
<p>结构还获得与类型同名的字段。
所以，如果嵌入的类型是 public，那么字段是 public。为了保持向后兼容性，外部类型的每个未来版本都必须保留嵌入类型。</p>
<p>很少需要嵌入类型。
这是一种方便，可以帮助您避免编写冗长的委托方法。</p>
<p>即使嵌入兼容的抽象列表 <em>interface</em>，而不是结构体，这将为开发人员提供更大的灵活性来改变未来，但仍然泄露了具体列表使用抽象实现的细节。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// AbstractList 是各种实体列表的通用实现。
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">AbstractList</span> <span class="kd">interface</span> <span class="p">{</span>
  <span class="nf">Add</span><span class="p">(</span><span class="nx">Entity</span><span class="p">)</span>
  <span class="nf">Remove</span><span class="p">(</span><span class="nx">Entity</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// ConcreteList 是一个实体列表。
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">ConcreteList</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">AbstractList</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// ConcreteList 是一个实体列表。
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">ConcreteList</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">list</span> <span class="o">*</span><span class="nx">AbstractList</span>
<span class="p">}</span>
<span class="c1">// 添加将实体添加到列表中。
</span><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">l</span> <span class="o">*</span><span class="nx">ConcreteList</span><span class="p">)</span> <span class="nf">Add</span><span class="p">(</span><span class="nx">e</span> <span class="nx">Entity</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">l</span><span class="p">.</span><span class="nx">list</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 移除从列表中移除实体。
</span><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">l</span> <span class="o">*</span><span class="nx">ConcreteList</span><span class="p">)</span> <span class="nf">Remove</span><span class="p">(</span><span class="nx">e</span> <span class="nx">Entity</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">l</span><span class="p">.</span><span class="nx">list</span><span class="p">.</span><span class="nf">Remove</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>无论是使用嵌入式结构还是使用嵌入式接口，嵌入式类型都会限制类型的演化.</p>
<ul>
<li>向嵌入式接口添加方法是一个破坏性的改变。</li>
<li>删除嵌入类型是一个破坏性的改变。</li>
<li>即使使用满足相同接口的替代方法替换嵌入类型，也是一个破坏性的改变。</li>
</ul>
<p>尽管编写这些委托方法是乏味的，但是额外的工作隐藏了实现细节，留下了更多的更改机会，还消除了在文档中发现完整列表接口的间接性操作。</p>
<h3 id="避免使用内置名称">避免使用内置名称</h3>
<p>Go语言规范<a href="https://golang.org/ref/spec">language specification</a> 概述了几个内置的，
不应在Go项目中使用的名称标识<a href="https://golang.org/ref/spec#Predeclared_identifiers">predeclared identifiers</a>。</p>
<p>根据上下文的不同，将这些标识符作为名称重复使用，
将在当前作用域（或任何嵌套作用域）中隐藏原始标识符，或者混淆代码。
在最好的情况下，编译器会报错；在最坏的情况下，这样的代码可能会引入潜在的、难以恢复的错误。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="kt">error</span> <span class="kt">string</span>
<span class="c1">// `error` 作用域隐式覆盖
</span><span class="c1"></span>
<span class="c1">// or
</span><span class="c1"></span>
<span class="kd">func</span> <span class="nf">handleErrorMessage</span><span class="p">(</span><span class="kt">error</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// `error` 作用域隐式覆盖
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">errorMessage</span> <span class="kt">string</span>
<span class="c1">// `error` 指向内置的非覆盖
</span><span class="c1"></span>
<span class="c1">// or
</span><span class="c1"></span>
<span class="kd">func</span> <span class="nf">handleErrorMessage</span><span class="p">(</span><span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// `error` 指向内置的非覆盖
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Foo</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="c1">// 虽然这些字段在技术上不构成阴影，但`error`或`string`字符串的重映射现在是不明确的。
</span><span class="c1"></span>    <span class="kt">error</span>  <span class="kt">error</span>
    <span class="kt">string</span> <span class="kt">string</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="nx">Foo</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
    <span class="c1">// `error` 和 `f.error` 在视觉上是相似的
</span><span class="c1"></span>    <span class="k">return</span> <span class="nx">f</span><span class="p">.</span><span class="kt">error</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="nx">Foo</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
    <span class="c1">// `string` and `f.string` 在视觉上是相似的
</span><span class="c1"></span>    <span class="k">return</span> <span class="nx">f</span><span class="p">.</span><span class="kt">string</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Foo</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="c1">// `error` and `string` 现在是明确的。
</span><span class="c1"></span>    <span class="nx">err</span> <span class="kt">error</span>
    <span class="nx">str</span> <span class="kt">string</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="nx">Foo</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">f</span><span class="p">.</span><span class="nx">err</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="nx">Foo</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">f</span><span class="p">.</span><span class="nx">str</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>注意，编译器在使用预先分隔的标识符时不会生成错误，
但是诸如<code>go vet</code>之类的工具会正确地指出这些和其他情况下的隐式问题。</p>
<h3 id="避免使用-init">避免使用 <code>init()</code></h3>
<p>尽可能避免使用<code>init()</code>。当<code>init()</code>是不可避免或可取的，代码应先尝试：</p>
<ol>
<li>无论程序环境或调用如何，都要完全确定。</li>
<li>避免依赖于其他<code>init()</code>函数的顺序或副作用。虽然<code>init()</code>顺序是明确的，但代码可以更改，
因此<code>init()</code>函数之间的关系可能会使代码变得脆弱和容易出错。</li>
<li>避免访问或操作全局或环境状态，如机器信息、环境变量、工作目录、程序参数/输入等。</li>
<li>避免<code>I/O</code>，包括文件系统、网络和系统调用。</li>
</ol>
<p>不能满足这些要求的代码可能属于要作为<code>main()</code>调用的一部分<code>（或程序生命周期中的其他地方）， 或者作为</code>main()`本身的一部分写入。特别是，打算由其他程序使用的库应该特别注意完全确定性，
而不是执行“init magic”</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Foo</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
<span class="kd">var</span> <span class="nx">_defaultFoo</span> <span class="nx">Foo</span>
<span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">_defaultFoo</span> <span class="p">=</span> <span class="nx">Foo</span><span class="p">{</span>
        <span class="c1">// ...
</span><span class="c1"></span>    <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">_defaultFoo</span> <span class="p">=</span> <span class="nx">Foo</span><span class="p">{</span>
    <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
<span class="c1">// or, 为了更好的可测试性:
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">_defaultFoo</span> <span class="p">=</span> <span class="nf">defaultFoo</span><span class="p">()</span>
<span class="kd">func</span> <span class="nf">defaultFoo</span><span class="p">()</span> <span class="nx">Foo</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">Foo</span><span class="p">{</span>
        <span class="c1">// ...
</span><span class="c1"></span>    <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Config</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
<span class="kd">var</span> <span class="nx">_config</span> <span class="nx">Config</span>
<span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// Bad: 基于当前目录
</span><span class="c1"></span>    <span class="nx">cwd</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getwd</span><span class="p">()</span>
    <span class="c1">// Bad: I/O
</span><span class="c1"></span>    <span class="nx">raw</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadFile</span><span class="p">(</span>
        <span class="nx">path</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="nx">cwd</span><span class="p">,</span> <span class="s">&#34;config&#34;</span><span class="p">,</span> <span class="s">&#34;config.yaml&#34;</span><span class="p">),</span>
    <span class="p">)</span>
    <span class="nx">yaml</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">raw</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">_config</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Config</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
<span class="kd">func</span> <span class="nf">loadConfig</span><span class="p">()</span> <span class="nx">Config</span> <span class="p">{</span>
    <span class="nx">cwd</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getwd</span><span class="p">()</span>
    <span class="c1">// handle err
</span><span class="c1"></span>    <span class="nx">raw</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadFile</span><span class="p">(</span>
        <span class="nx">path</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="nx">cwd</span><span class="p">,</span> <span class="s">&#34;config&#34;</span><span class="p">,</span> <span class="s">&#34;config.yaml&#34;</span><span class="p">),</span>
    <span class="p">)</span>
    <span class="c1">// handle err
</span><span class="c1"></span>    <span class="kd">var</span> <span class="nx">config</span> <span class="nx">Config</span>
    <span class="nx">yaml</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">raw</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">config</span><span class="p">)</span>
    <span class="k">return</span> <span class="nx">config</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>考虑到上述情况，在某些情况下，<code>init()</code>可能更可取或是必要的，可能包括：</p>
<ul>
<li>
<p>不能表示为单个赋值的复杂表达式。</p>
</li>
<li>
<p>可插入的钩子，如<code>database/sql</code>、编码类型注册表等。</p>
</li>
<li>
<p>对<a href="https://cloud.google.com/functions/docs/bestpractices/tips#use_global_variables_to_reuse_objects_in_future_invocations">Google Cloud Functions</a>和其他形式的确定性预计算的优化。</p>
</li>
</ul>
<h3 id="追加时优先指定切片容量">追加时优先指定切片容量</h3>
<p>追加时优先指定切片容量</p>
<p>在尽可能的情况下，在初始化要追加的切片时为<code>make()</code>提供一个容量值。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">n</span> <span class="p">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
  <span class="nx">data</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
  <span class="k">for</span> <span class="nx">k</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">k</span> <span class="p">&lt;</span> <span class="nx">size</span><span class="p">;</span> <span class="nx">k</span><span class="o">++</span><span class="p">{</span>
    <span class="nx">data</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">k</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">n</span> <span class="p">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
  <span class="nx">data</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">size</span><span class="p">)</span>
  <span class="k">for</span> <span class="nx">k</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">k</span> <span class="p">&lt;</span> <span class="nx">size</span><span class="p">;</span> <span class="nx">k</span><span class="o">++</span><span class="p">{</span>
    <span class="nx">data</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">k</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">BenchmarkBad-4    100000000    2.48s
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">BenchmarkGood-4   100000000    0.21s
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h2 id="性能">性能</h2>
<p>性能方面的特定准则只适用于高频场景。</p>
<h3 id="优先使用-strconv-而不是-fmt">优先使用 strconv 而不是 fmt</h3>
<p>将原语转换为字符串或从字符串转换时，<code>strconv</code>速度比<code>fmt</code>快。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
  <span class="nx">s</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Int</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
  <span class="nx">s</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Itoa</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Int</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">BenchmarkFmtSprint-4    143 ns/op    2 allocs/op
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">BenchmarkStrconv-4    64.2 ns/op    1 allocs/op
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="避免字符串到字节的转换">避免字符串到字节的转换</h3>
<p>不要反复从固定字符串创建字节 slice。相反，请执行一次转换并捕获结果。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
  <span class="nx">w</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;Hello world&#34;</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">data</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;Hello world&#34;</span><span class="p">)</span>
<span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
  <span class="nx">w</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></tr>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">BenchmarkBad-4   50000000   22.2 ns/op
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">BenchmarkGood-4  500000000   3.25 ns/op
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h3 id="指定容器容量">指定容器容量</h3>
<p>尽可能指定容器容量，以便为容器预先分配内存。这将在添加元素时最小化后续分配（通过复制和调整容器大小）。</p>
<h4 id="指定map容量提示">指定Map容量提示</h4>
<p>在尽可能的情况下，在使用 <code>make()</code> 初始化的时候提供容量信息</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="nx">T1</span><span class="p">]</span><span class="nx">T2</span><span class="p">,</span> <span class="nx">hint</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p>向<code>make()</code>提供容量提示会在初始化时尝试调整map的大小，这将减少在将元素添加到map时为map重新分配内存。</p>
<p>注意，与slices不同。map capacity提示并不保证完全的抢占式分配，而是用于估计所需的hashmap bucket的数量。
因此，在将元素添加到map时，甚至在指定map容量时，仍可能发生分配。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">m</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">os</span><span class="p">.</span><span class="nx">FileInfo</span><span class="p">)</span>

<span class="nx">files</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadDir</span><span class="p">(</span><span class="s">&#34;./files&#34;</span><span class="p">)</span>
<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">f</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">files</span> <span class="p">{</span>
    <span class="nx">m</span><span class="p">[</span><span class="nx">f</span><span class="p">.</span><span class="nf">Name</span><span class="p">()]</span> <span class="p">=</span> <span class="nx">f</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go">
<span class="nx">files</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadDir</span><span class="p">(</span><span class="s">&#34;./files&#34;</span><span class="p">)</span>

<span class="nx">m</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">os</span><span class="p">.</span><span class="nx">FileInfo</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">files</span><span class="p">))</span>
<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">f</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">files</span> <span class="p">{</span>
    <span class="nx">m</span><span class="p">[</span><span class="nx">f</span><span class="p">.</span><span class="nf">Name</span><span class="p">()]</span> <span class="p">=</span> <span class="nx">f</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<p><code>m</code> 是在没有大小提示的情况下创建的； 在运行时可能会有更多分配。</p>
</td><td>
<p><code>m</code> 是有大小提示创建的；在运行时可能会有更少的分配。</p>
</td></tr>
</tbody></table>
<h4 id="指定切片容量">指定切片容量</h4>
<p>在尽可能的情况下，在使用<code>make()</code>初始化切片时提供容量信息，特别是在追加切片时。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nb">make</span><span class="p">([]</span><span class="nx">T</span><span class="p">,</span> <span class="nx">length</span><span class="p">,</span> <span class="nx">capacity</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p>与maps不同，slice capacity不是一个提示：编译器将为提供给<code>make()</code>的slice的容量分配足够的内存，
这意味着后续的append()`操作将导致零分配（直到slice的长度与容量匹配，在此之后，任何append都可能调整大小以容纳其他元素）。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">n</span> <span class="p">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
  <span class="nx">data</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
  <span class="k">for</span> <span class="nx">k</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">k</span> <span class="p">&lt;</span> <span class="nx">size</span><span class="p">;</span> <span class="nx">k</span><span class="o">++</span><span class="p">{</span>
    <span class="nx">data</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">k</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">n</span> <span class="p">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
  <span class="nx">data</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">size</span><span class="p">)</span>
  <span class="k">for</span> <span class="nx">k</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">k</span> <span class="p">&lt;</span> <span class="nx">size</span><span class="p">;</span> <span class="nx">k</span><span class="o">++</span><span class="p">{</span>
    <span class="nx">data</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">k</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">BenchmarkBad-4    100000000    2.48s
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">BenchmarkGood-4   100000000    0.21s
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<h2 id="编程模式">编程模式</h2>
<h3 id="表驱动测试">表驱动测试</h3>
<p>当测试逻辑是重复的时候，通过  <a href="https://blog.golang.org/subtests">subtests</a> 使用 table 驱动的方式编写 case 代码看上去会更简洁。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// func TestSplitHostPort(t *testing.T)
</span><span class="c1"></span>
<span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">SplitHostPort</span><span class="p">(</span><span class="s">&#34;192.0.2.0:8000&#34;</span><span class="p">)</span>
<span class="nx">require</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;192.0.2.0&#34;</span><span class="p">,</span> <span class="nx">host</span><span class="p">)</span>
<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;8000&#34;</span><span class="p">,</span> <span class="nx">port</span><span class="p">)</span>

<span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">SplitHostPort</span><span class="p">(</span><span class="s">&#34;192.0.2.0:http&#34;</span><span class="p">)</span>
<span class="nx">require</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;192.0.2.0&#34;</span><span class="p">,</span> <span class="nx">host</span><span class="p">)</span>
<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;http&#34;</span><span class="p">,</span> <span class="nx">port</span><span class="p">)</span>

<span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">SplitHostPort</span><span class="p">(</span><span class="s">&#34;:8000&#34;</span><span class="p">)</span>
<span class="nx">require</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="nx">host</span><span class="p">)</span>
<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;8000&#34;</span><span class="p">,</span> <span class="nx">port</span><span class="p">)</span>

<span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">SplitHostPort</span><span class="p">(</span><span class="s">&#34;1:8&#34;</span><span class="p">)</span>
<span class="nx">require</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;1&#34;</span><span class="p">,</span> <span class="nx">host</span><span class="p">)</span>
<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;8&#34;</span><span class="p">,</span> <span class="nx">port</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// func TestSplitHostPort(t *testing.T)
</span><span class="c1"></span>
<span class="nx">tests</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span><span class="p">{</span>
  <span class="nx">give</span>     <span class="kt">string</span>
  <span class="nx">wantHost</span> <span class="kt">string</span>
  <span class="nx">wantPort</span> <span class="kt">string</span>
<span class="p">}{</span>
  <span class="p">{</span>
    <span class="nx">give</span><span class="p">:</span>     <span class="s">&#34;192.0.2.0:8000&#34;</span><span class="p">,</span>
    <span class="nx">wantHost</span><span class="p">:</span> <span class="s">&#34;192.0.2.0&#34;</span><span class="p">,</span>
    <span class="nx">wantPort</span><span class="p">:</span> <span class="s">&#34;8000&#34;</span><span class="p">,</span>
  <span class="p">},</span>
  <span class="p">{</span>
    <span class="nx">give</span><span class="p">:</span>     <span class="s">&#34;192.0.2.0:http&#34;</span><span class="p">,</span>
    <span class="nx">wantHost</span><span class="p">:</span> <span class="s">&#34;192.0.2.0&#34;</span><span class="p">,</span>
    <span class="nx">wantPort</span><span class="p">:</span> <span class="s">&#34;http&#34;</span><span class="p">,</span>
  <span class="p">},</span>
  <span class="p">{</span>
    <span class="nx">give</span><span class="p">:</span>     <span class="s">&#34;:8000&#34;</span><span class="p">,</span>
    <span class="nx">wantHost</span><span class="p">:</span> <span class="s">&#34;&#34;</span><span class="p">,</span>
    <span class="nx">wantPort</span><span class="p">:</span> <span class="s">&#34;8000&#34;</span><span class="p">,</span>
  <span class="p">},</span>
  <span class="p">{</span>
    <span class="nx">give</span><span class="p">:</span>     <span class="s">&#34;1:8&#34;</span><span class="p">,</span>
    <span class="nx">wantHost</span><span class="p">:</span> <span class="s">&#34;1&#34;</span><span class="p">,</span>
    <span class="nx">wantPort</span><span class="p">:</span> <span class="s">&#34;8&#34;</span><span class="p">,</span>
  <span class="p">},</span>
<span class="p">}</span>

<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tt</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tests</span> <span class="p">{</span>
  <span class="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="nx">tt</span><span class="p">.</span><span class="nx">give</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">SplitHostPort</span><span class="p">(</span><span class="nx">tt</span><span class="p">.</span><span class="nx">give</span><span class="p">)</span>
    <span class="nx">require</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
    <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">wantHost</span><span class="p">,</span> <span class="nx">host</span><span class="p">)</span>
    <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">wantPort</span><span class="p">,</span> <span class="nx">port</span><span class="p">)</span>
  <span class="p">})</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>很明显，使用 test table 的方式在代码逻辑扩展的时候，比如新增 test case，都会显得更加的清晰。</p>
<p>我们遵循这样的约定：将结构体切片称为<code>tests</code>。 每个测试用例称为<code>tt</code>。此外，我们鼓励使用<code>give</code>和<code>want</code>前缀说明每个测试用例的输入和输出值。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">tests</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span><span class="p">{</span>
  <span class="nx">give</span>     <span class="kt">string</span>
  <span class="nx">wantHost</span> <span class="kt">string</span>
  <span class="nx">wantPort</span> <span class="kt">string</span>
<span class="p">}{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>

<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tt</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tests</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h3 id="功能选项">功能选项</h3>
<p>功能选项是一种模式，您可以在其中声明一个不透明 Option 类型，该类型在某些内部结构中记录信息。您接受这些选项的可变编号，并根据内部结构上的选项记录的全部信息采取行动。</p>
<p>将此模式用于您需要扩展的构造函数和其他公共 API 中的可选参数，尤其是在这些功能上已经具有三个或更多参数的情况下。</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// package db
</span><span class="c1"></span>
<span class="kd">func</span> <span class="nf">Open</span><span class="p">(</span>
  <span class="nx">addr</span> <span class="kt">string</span><span class="p">,</span>
  <span class="nx">cache</span> <span class="kt">bool</span><span class="p">,</span>
  <span class="nx">logger</span> <span class="o">*</span><span class="nx">zap</span><span class="p">.</span><span class="nx">Logger</span>
<span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">Connection</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// package db
</span><span class="c1"></span>
<span class="kd">type</span> <span class="nx">Option</span> <span class="kd">interface</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>

<span class="kd">func</span> <span class="nf">WithCache</span><span class="p">(</span><span class="nx">c</span> <span class="kt">bool</span><span class="p">)</span> <span class="nx">Option</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>

<span class="kd">func</span> <span class="nf">WithLogger</span><span class="p">(</span><span class="nx">log</span> <span class="o">*</span><span class="nx">zap</span><span class="p">.</span><span class="nx">Logger</span><span class="p">)</span> <span class="nx">Option</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>

<span class="c1">// Open creates a connection.
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">Open</span><span class="p">(</span>
  <span class="nx">addr</span> <span class="kt">string</span><span class="p">,</span>
  <span class="nx">opts</span> <span class="o">...</span><span class="nx">Option</span><span class="p">,</span>
<span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">Connection</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
<tr><td>
<p>必须始终提供缓存和记录器参数，即使用户希望使用默认值。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">db</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">db</span><span class="p">.</span><span class="nx">DefaultCache</span><span class="p">,</span> <span class="nx">zap</span><span class="p">.</span><span class="nf">NewNop</span><span class="p">())</span>
<span class="nx">db</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">db</span><span class="p">.</span><span class="nx">DefaultCache</span><span class="p">,</span> <span class="nx">log</span><span class="p">)</span>
<span class="nx">db</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="kc">false</span> <span class="cm">/* cache */</span><span class="p">,</span> <span class="nx">zap</span><span class="p">.</span><span class="nf">NewNop</span><span class="p">())</span>
<span class="nx">db</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="kc">false</span> <span class="cm">/* cache */</span><span class="p">,</span> <span class="nx">log</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td><td>
<p>只有在需要时才提供选项。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">db</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">addr</span><span class="p">)</span>
<span class="nx">db</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">db</span><span class="p">.</span><span class="nf">WithLogger</span><span class="p">(</span><span class="nx">log</span><span class="p">))</span>
<span class="nx">db</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">db</span><span class="p">.</span><span class="nf">WithCache</span><span class="p">(</span><span class="kc">false</span><span class="p">))</span>
<span class="nx">db</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span>
  <span class="nx">addr</span><span class="p">,</span>
  <span class="nx">db</span><span class="p">.</span><span class="nf">WithCache</span><span class="p">(</span><span class="kc">false</span><span class="p">),</span>
  <span class="nx">db</span><span class="p">.</span><span class="nf">WithLogger</span><span class="p">(</span><span class="nx">log</span><span class="p">),</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div></td></tr>
</tbody></table>
<p>Our suggested way of implementing this pattern is with an <code>Option</code> interface
that holds an unexported method, recording options on an unexported <code>options</code>
struct.</p>
<p>我们建议实现此模式的方法是使用一个 <code>Option</code> 接口，该接口保存一个未导出的方法，在一个未导出的 <code>options</code> 结构上记录选项。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">options</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">cache</span>  <span class="kt">bool</span>
  <span class="nx">logger</span> <span class="o">*</span><span class="nx">zap</span><span class="p">.</span><span class="nx">Logger</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">Option</span> <span class="kd">interface</span> <span class="p">{</span>
  <span class="nf">apply</span><span class="p">(</span><span class="o">*</span><span class="nx">options</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">cacheOption</span> <span class="kt">bool</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">cacheOption</span><span class="p">)</span> <span class="nf">apply</span><span class="p">(</span><span class="nx">opts</span> <span class="o">*</span><span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">opts</span><span class="p">.</span><span class="nx">cache</span> <span class="p">=</span> <span class="nb">bool</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">WithCache</span><span class="p">(</span><span class="nx">c</span> <span class="kt">bool</span><span class="p">)</span> <span class="nx">Option</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nf">cacheOption</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">loggerOption</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">Log</span> <span class="o">*</span><span class="nx">zap</span><span class="p">.</span><span class="nx">Logger</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">l</span> <span class="nx">loggerOption</span><span class="p">)</span> <span class="nf">apply</span><span class="p">(</span><span class="nx">opts</span> <span class="o">*</span><span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">opts</span><span class="p">.</span><span class="nx">logger</span> <span class="p">=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">Log</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">WithLogger</span><span class="p">(</span><span class="nx">log</span> <span class="o">*</span><span class="nx">zap</span><span class="p">.</span><span class="nx">Logger</span><span class="p">)</span> <span class="nx">Option</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">loggerOption</span><span class="p">{</span><span class="nx">Log</span><span class="p">:</span> <span class="nx">log</span><span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Open creates a connection.
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">Open</span><span class="p">(</span>
  <span class="nx">addr</span> <span class="kt">string</span><span class="p">,</span>
  <span class="nx">opts</span> <span class="o">...</span><span class="nx">Option</span><span class="p">,</span>
<span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">Connection</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">options</span> <span class="o">:=</span> <span class="nx">options</span><span class="p">{</span>
    <span class="nx">cache</span><span class="p">:</span>  <span class="nx">defaultCache</span><span class="p">,</span>
    <span class="nx">logger</span><span class="p">:</span> <span class="nx">zap</span><span class="p">.</span><span class="nf">NewNop</span><span class="p">(),</span>
  <span class="p">}</span>

  <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">o</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">opts</span> <span class="p">{</span>
    <span class="nx">o</span><span class="p">.</span><span class="nf">apply</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">options</span><span class="p">)</span>
  <span class="p">}</span>

  <span class="c1">// ...
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>注意: 还有一种使用闭包实现这个模式的方法，但是我们相信上面的模式为作者提供了更多的灵活性，并且更容易对用户进行调试和测试。特别是，在不可能进行比较的情况下它允许在测试和模拟中对选项进行比较。此外，它还允许选项实现其他接口，包括 <code>fmt.Stringer</code>，允许用户读取选项的字符串表示形式。</p>
<p>还可以参考下面资料：</p>
<ul>
<li>
<p><a href="https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html">Self-referential functions and the design of options</a></p>
</li>
<li>
<p><a href="https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis">Functional options for friendly APIs</a></p>
</li>
</ul>
<!-- TODO: replace this with parameter structs and functional options, when to
use one vs other -->
<h2 id="其他要求">其他要求</h2>
<h3 id="通过检查">通过检查</h3>
<ul>
<li>
<p>各IDE自带的GoLint 插件</p>
</li>
<li>
<p>go tool vet检查</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell">go tool vet &lt;directory<span class="p">|</span>files&gt;
</code></pre></td></tr></table>
</div>
</div></li>
</ul>
<h3 id="尽可能完善单元测试">尽可能完善单元测试</h3>
<ul>
<li>Go语言官方则提供了语言级的单元测试支持，即testing包，而且仅通过go工具本身就可以方便地生成覆盖率数据。</li>
<li>若实现了单元测试，需要统计代码覆盖率，建议覆盖率在80%以上。</li>
</ul>
<h3 id="完整的包注释">完整的包注释</h3>
<ul>
<li>每个包都应该有一个包注释，一个位于 package 子句之前的块注释或行注释。包如果有多个 go 文件，只需要出现在一个 go 文件中（一般是和包同名的文件）即可。 包注释应该包含下面基本信息(请严格按照这个顺序，简介，创建人，创建时间）：</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="c1">// @Title 
</span><span class="c1">// @Description 
</span><span class="c1">// @Author 创建人 创建时间
</span><span class="c1">// @Update  创建人 修改时间
</span><span class="c1"></span><span class="kn">package</span> <span class="err">$</span><span class="p">{</span><span class="nx">GO_PACKAGE_NAME</span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h3 id="自定义接口结构体函数注释">自定义接口、结构体、函数注释</h3>
<ul>
<li>对外暴露的函数应当详细注明作用，入参和出参</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">// @title    函数名称
// @description   函数的详细描述
// @auth      作者             时间（2019/6/18   10:57 ）
// @param     输入参数名        参数类型         &#34;解释&#34;
// @return    返回参数名        参数类型         &#34;解释&#34;
</code></pre></td></tr></table>
</div>
</div><ul>
<li>
<p>对外暴露的结构或者接口应当注明作用、成员</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback">// User   用户对象，定义了用户的基础信息
type User struct{
    Username  string // 用户名
    Email     string // 邮箱
}
</code></pre></td></tr></table>
</div>
</div></li>
</ul>

    </div>

    <div class="post-copyright">
  <p class="copyright-item">
    <span class="item-title">文章作者</span>
    <span class="item-content">ZhangKQ</span>
  </p>
  <p class="copyright-item">
    <span class="item-title">上次更新</span>
    <span class="item-content">
        2022-02-10
        
    </span>
  </p>
  
  
</div>
<div class="post-reward">
  <input type="checkbox" name="reward" id="reward" hidden />
  <label class="reward-button" for="reward">赞赏支持</label>
  <div class="qr-code">
    
    <label class="qr-code-image" for="reward">
        <img class="image" src="/qrcode/wechat-qr-code.jpg">
        <span>微信打赏</span>
      </label>
    <label class="qr-code-image" for="reward">
        <img class="image" src="/qrcode/alipay-qr-code.jpg">
        <span>支付宝打赏</span>
      </label>
  </div>
</div><footer class="post-footer">
      <div class="post-tags">
          <a href="/tags/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/">开发语言</a>
          <a href="/tags/%E5%90%8E%E7%AB%AF/">后端</a>
          <a href="/tags/golang/">golang</a>
          <a href="/tags/go%E8%AF%AD%E8%A8%80/">go语言</a>
          <a href="/tags/%E8%AF%AD%E8%A8%80%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83/">语言编码规范</a>
          <a href="/tags/uber/">Uber</a>
          </div>
      <nav class="post-nav">
        <a class="prev" href="/post/dev_language/golang/golang%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/">
            <i class="iconfont icon-left"></i>
            <span class="prev-text nav-default">[go] Golang开发规范</span>
            <span class="prev-text nav-mobile">上一篇</span>
          </a>
        <a class="next" href="/post/version_controller/git/%E4%BC%98%E5%8C%96github%E6%89%93%E5%BC%80%E9%80%9F%E5%BA%A6-%E5%89%AF%E6%9C%AC/">
            <span class="next-text nav-default">[git]优化github打开速度(解决无法打开或者打开慢的问题)</span>
            <span class="next-text nav-mobile">下一篇</span>
            <i class="iconfont icon-right"></i>
          </a>
      </nav>
    </footer>
  </article>
        </div>
        

  

  

      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="social-links">
      <a href="mailto:wdyxzkq@163.com" class="iconfont icon-email" title="email"></a>
      <a href="https://github.com/dysoso" class="iconfont icon-github" title="github"></a>
      <a href="https://gitee.com/dysoso" class="iconfont icon-gitlab" title="gitlab"></a>
  <a href="https://blog.nevergiveup.tech/index.xml" type="application/rss+xml" class="iconfont icon-rss" title="rss"></a>
</div>

<div class="copyright">
  <span class="power-by">
    由 <a class="hexo-link" href="https://blog.nevergiveup.tech/">blog.nevergiveup.tech</a> 强力驱动
  </span>
  <span class="division">|</span>
  <span class="theme-info">
    主题 - 
    <a class="theme-link" href="https://github.com/olOwOlo/hugo-theme-even">Even</a>
  </span>

  

  <span class="copyright-year">
    &copy; 
    2021 - 
    2022<span class="heart"><i class="iconfont icon-heart"></i></span><span><a href="https://beian.miit.gov.cn/">蜀ICP备2021005948号-1</a></span>
  </span>
</div>

    </footer>

    <div class="back-to-top" id="back-to-top">
      <i class="iconfont icon-up"></i>
    </div>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/slideout@1.0.1/dist/slideout.min.js" integrity="sha256-t+zJ/g8/KXIJMjSVQdnibt4dlaDxc9zXr/9oNPeWqdg=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.js" integrity="sha256-XVLffZaxoWfGUEbdzuLi7pwaUJv1cecsQJQqGLe7axY=" crossorigin="anonymous"></script>



<script type="text/javascript" src="/js/main.min.c99b103c33d1539acf3025e1913697534542c4a5aa5af0ccc20475ed2863603b.js"></script>


<script type="application/javascript">
var doNotTrack = false;
if (!doNotTrack) {
	window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
	ga('create', 'never-give-up', 'auto');
	ga('set', 'anonymizeIp', true);
	ga('send', 'pageview');
}
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>







</body>
</html>
